Introduction

The Philadelphia housing market is a dynamic and diverse landscape influenced by various factors that impact both prices and overall market conditions. This market reflects the economic and social dynamics of the city and the surrounding region.It encompasses a wide range of neighborhoods, each with its unique character, amenities, and housing styles. The city’s diverse housing stock includes rowhouses, townhomes, apartments, and single-family homes, catering to a variety of lifestyles and preferences.

Housing prices in Philadelphia have experienced fluctuations over the years, influenced by a combination of local and national factors. However, it’s important to note that these prices can vary significantly based on factors such as the neighborhood, property type, and condition of the home. This project aims to evaluate the various factors affecting housing prices and create an accurate and generalizable OLS regression model to predict the housing prices in Philadelphia.

The process of creating this model was extremely iterative, and involved multiple rounds of trial and error, particularly in attempts to minimize the errors in predictions. The maps, correlation matrix and scatterplots are all illustrative of variables that were considered, wrangled and filtered as part of the data analysis process.

This code is built upon the classwork discussed here.

R Setup and Installing Packages

This code chunk handles the essential tasks of loading necessary packages, configuring the Census API key, defining class functions, specifying a color palette, and managing global environment settings.

# Loading libraries

library(tidyverse)
library(tidycensus)
library(sf)
library(kableExtra)
library(tidyr)
library(ggplot2)
library(viridis)
library(stringr)
library(tigris)
library(ggcorrplot)
library(stargazer)
library(dplyr)
library(caTools)
library(spdep)
library(caret)
library(jtools)     # for regression model plots
library(ggstance) # to support jtools plots
library(ggpubr)    # plotting R^2 value on ggplot point scatter
library(broom.mixed) # needed for effects plots

# Setting parameters for scientific notation

options(scipen=999)
options(tigris_class = "sf")

# Functions and data directory

source("https://raw.githubusercontent.com/urbanSpatial/Public-Policy-Analytics-Landing/master/functions.r")

# Invoking color palettes to be used

palettee <- c('#d7191c','#fdae61','#ffffbf','#abd9e9','#2c7bb6')

# Registering API Key to be used

census_api_key('bf2d507651b5a621dbadd44533fb4f3deaab26bf', overwrite = TRUE)

Data Wrangling

Data provided was cleaned and new variable were created

# Reading Data

data <- 
  st_read("./data/studentData.geojson") %>%
  st_transform('ESRI:102286')

# Dropping columns with no values and filtering values within Philadelphia

data <-  data %>% 
  select(-cross_reference, -date_exterior_condition, -mailing_address_2, -mailing_care_of, -unfinished, -utility, -category_code, -category_code_description, -exempt_land, -separate_utilities, -sewer, - site_type, -house_extension, -street_direction, -suffix, -garage_type, -general_construction )%>% 
  filter(mailing_city_state == "PHILADELPHIA PA" )

Missing Data and Categorical Variables

## Filtering out 9 rows where year built is not given

data <-  data %>% 
  filter(year_built > 0 )

## Categorizing if a Basement is present

data <- data %>%
  mutate(BasementPresent = case_when(basements == 'A' |
  basements == 'B' |
  basements == 'C' |
  basements == 'D' |
  basements == 'E' |
  basements == 'F' |
  basements == 'G' |
  basements == 'H' |
  basements == 'I' |
  basements == 'J' ~ 1,
  basements == '0' ~ 0))

# Assigning value of 0 to 'NA' rows based on description in metadata

data$BasementPresent[is.na(data$BasementPresent)] <- 0

## Categorizing Basement Type

library(dplyr)

data <- data %>%
  mutate(BasementType = case_when(basements == 'A' ~ 'Full size finished',
  basements == 'B' ~ 'Full size semi-finished',
  basements == 'C' ~ 'Full size unfinished',
  basements == 'D' ~ 'Full size unknown finish',
  basements == 'E' ~ 'Partial size finished',
  basements == 'F' ~ 'Partial size semi-finished',
  basements == 'G' ~ 'Partial size unfinished',
  basements == 'H' ~ 'Partial size unknown finish',
  basements == 'I' ~ 'Unknown size finished',
  basements == 'J' ~ 'Unknown size unfinished',
  basements == '0' ~ 'No basement'))

# Assigning value of 0 to 'NA' rows based on description in metadata

data$BasementType[is.na(data$BasementType)] <- "No basement"
data$basements[is.na(data$basements)] <- 0

## Categorizing based on Central Air

data$central_air <- ifelse(data$central_air == 'Y', 1, 0)

## Categorizing based on Exterior Condition

data <- data %>%
  mutate(ExteriorConditionType = case_when(exterior_condition == '1' |
  exterior_condition == '2' |
  exterior_condition == '3' ~ 'Good Condition',
  exterior_condition == '4' |
  exterior_condition == '5' ~ 'Average Condition',
  exterior_condition == '6' ~ 'Below Average Condition',
  exterior_condition == '7' ~ 'Vacant and Sealed'))

# Assigning value to single 'NA' row based on 'interior_construction' rating for same row

data$ExteriorConditionType[is.na(data$ExteriorConditionType)] <- "Good Condition"
data$exterior_condition[is.na(data$exterior_condition)] <- 3

## Categorizing based on Fireplaces

# Assigning value of 0 to 'NA' rows based on description in metadata

data$fireplaces[is.na(data$fireplaces)] <- 0

## Categorizing based on Fuel Sources

data <- data %>%
  mutate(FuelType = case_when(fuel == 'A' ~ 'Natural Gas Powered',
  fuel == 'B' ~ 'Oil Powered',
  fuel == 'C' ~ 'Electric Powered',
  fuel == 'E' ~ 'Solar Powered',
  fuel == 'G' ~ 'Fuel Source Unknown'))

# Assigning value of Unknown/G to 'NA' rows based on description in metadata

data$FuelType[is.na(data$FuelType)] <- "Fuel Source Unknown"
data$fuel[is.na(data$fuel)] <- "G"

## Categorizing based on Garage Presence

data <- data %>%
  mutate(GaragePresent = case_when(garage_spaces == '0' ~ 0,
  garage_spaces == '1' |
  garage_spaces == '2' |
  garage_spaces == '3' ~ 1))

# Assigning value of 0 to 'NA' rows based on description in metadata

data$GaragePresent[is.na(data$GaragePresent)] <- 0
data$garage_spaces[is.na(data$garage_spaces)] <- 0

## Categorizing based on Garage Types

data <- data %>%
  mutate(GarageType = case_when(garage_spaces == '0' ~ 'No Garage',
  garage_spaces == '1' ~ 'Single Garage',
  garage_spaces == '2' |
  garage_spaces == '3' ~ 'Multiple Garages'))

# Assigning value of 0 to 'NA' rows based on description in metadata

data$GarageType[is.na(data$GarageType)] <- "No Garage"
data$garage_spaces[is.na(data$garage_spaces)] <- 0


data <- data %>%
  mutate(InteriorConditionType = case_when( interior_condition == '0' |
  interior_condition == '1' |
  interior_condition == '2' |
  interior_condition == '3' ~ 'Good Condition',
  interior_condition == '4' ~ 'Average Condition',
  interior_condition == '5' ~ 'Below Average Condition',
  interior_condition == '6' |
  interior_condition == '7' ~ 'Vacant and/or Sealed'))

# Assigning values to 0 or 'NA' rows based on exterior condition since interior and exterior conditions are equal in almost all cases

data$InteriorConditionType[is.na(data$InteriorConditionType)] <- c(data$ExteriorConditionType)

## Categorizing based on View Type

data <- data %>%
  mutate(ViewType = case_when(view_type == '0' ~ 'Nature of View Unknown',
  view_type == 'I' ~ 'Typical View',
  view_type == 'A' ~ 'Skyline View',
  view_type == 'B' ~ 'River View',
  view_type == 'C' ~ 'Park View',
  view_type == 'D' ~ 'Commercial Area View',
  view_type == 'E' ~ 'Industrial Area View',
  view_type == 'H' ~ 'View of Landmark'))

# Assigning value to NA rows to nature of view unknown

data$ViewType[is.na(data$ViewType)] <- "Nature of View Unknown"
data$view_type[is.na(data$view_type)] <- 0

## Categorizing based on Topography Type

data <- data %>%
  mutate(TopographyType = case_when(topography == 'A' ~ 'Above Street Level Topography',
  topography == 'B' ~ 'Below Street Level Topography',
  topography == 'C' ~ 'Flood Plain Topography',
  topography == 'D' ~ 'Rocky Topography',
  topography == 'E' ~ 'Other Topography',
  topography == 'F' ~ 'Level Topography'))

# Assigning value to NA rows to nature of view unknown

data$TopographyType[is.na(data$TopographyType)] <- "Topography Unknown"
data$topography[is.na(data$topography)] <- 0

## Categorizing based on Parcel Type

data <- data %>%
  mutate(ParcelType = case_when(parcel_shape == 'A' ~ 'Irregular Parcel',
  parcel_shape == 'B' ~ 'Grossly Irregular Parcel',
  parcel_shape == 'C' ~ 'Triangular Parcel',
  parcel_shape == 'D' ~ 'Long Narrow Parcel',
  parcel_shape == 'E' ~ 'Rectangular Parcel'))

# Assigning value to NA rows to nature of view unknown

data$ParcelType[is.na(data$ParcelType)] <- "Parcel Type Unknown"
data$parcel_shape[is.na(data$parcel_shape)] <- 0
## Imputing values for missing values of number of bedrooms based on total livable area

# Dropping 32 values of total livable area which are 0 for better prediction

data <-  data %>% 
  filter(total_livable_area > 0 )

# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of bathrooms and rows that do not

data <- data %>%
  mutate(BedroomIndex = case_when(number_of_bedrooms >= 1 ~ 1,
                               number_of_bedrooms < 1 ~ 0))
                               
data$BedroomIndex[is.na(data$BedroomIndex)] <- 0

# Step 2 - Creating a linear regression model relating number of bedrooms and total liveable area

lm(number_of_bedrooms ~ total_livable_area, data=data)

# Step 3 - Imputing new values for missing values of number of bedrooms using regression results

for(i in 1:nrow(data))
{
  if (data$BedroomIndex[i] == 0)
  {
    data$number_of_bedrooms[i] = 2.1605650 + 0.0003057*data$total_livable_area[i]
  }
  }

data$number_of_bedrooms <- round(data$number_of_bedrooms, digits=0)
## Imputing values for missing values of number of rooms based on total livable area

# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of rooms and rows that do not

data <- data %>%
  mutate(RoomIndex = case_when(number_of_rooms >= 1 ~ 1,
                               number_of_rooms < 1 ~ 0))
                               
data$RoomIndex[is.na(data$RoomIndex)] <- 0

# Step 2 - Creating a linear regression model relating number of rooms and total livable area

lm(number_of_rooms ~ total_livable_area, data=data)

# Step 3 - Imputing new values for missing values of number of rooms using regression results

for(i in 1:nrow(data))
{
  if (data$RoomIndex[i] == 0)
  {
    data$number_of_rooms[i] = 4.316503 + 0.001319*data$total_livable_area[i]
  }
  }

data$number_of_rooms <- round(data$number_of_rooms, digits=0)
## Imputing values for missing values of number of bathrooms based on total livable area

# Step 1 - Creating an index of 0 and 1 values for rows that have values for number of bathrooms and rows that do not

data <- data %>%
  mutate(BathroomIndex = case_when(number_of_bathrooms >= 1 ~ 1,
                               number_of_bathrooms < 1 ~ 0))

data$BathroomIndex[is.na(data$BathroomIndex)] <- 0

# Step 2 - Creating a linear regression model relating number of bathrooms and  total livable area

lm(number_of_bathrooms ~ total_livable_area, data=data)

# Step 3 - Imputing new values for missing values of number of bathrooms using regression results

for(i in 1:nrow(data))
{
  if (data$BathroomIndex[i] == 0)
  {
    data$number_of_bathrooms[i] = 0.6426310 + 0.0003283*data$total_livable_area[i]
  }
  }

data$number_of_bathrooms <- round(data$number_of_bathrooms, digits=0)
data <- 
  data %>%
  mutate(PricePerSqft = (sale_price/total_livable_area))

data <- 
  data %>%
  mutate(BuildingAge = (2023 - (year_built)))
data <- data %>% 
  select(objectid, assessment_date, BuildingAge, year_built, building_code, building_code_description, pin, building_code_new, building_code_description_new,  census_tract, geographic_ward, zoning, location, street_name, street_code, street_designation, zip_code, house_number, depth, frontage, central_air, fireplaces, fuel, FuelType, basements, BasementPresent, BasementType, garage_spaces, GaragePresent, GarageType, exterior_condition, ExteriorConditionType, interior_condition, InteriorConditionType, number_of_bathrooms, number_of_bedrooms, number_of_rooms, number_stories, total_livable_area, view_type, ViewType, topography, TopographyType, parcel_shape, ParcelType, sale_date, sale_year, sale_price, PricePerSqft, musaID, toPredict, geometry )
data <-  data %>% 
  filter(number_of_bedrooms <10, number_of_rooms<15, ((number_of_bathrooms+number_of_bedrooms) < number_of_rooms), PricePerSqft<1500, total_livable_area<10000)

Census Data

The years chosen for analysis are 2021 because covid recovery most recent to mkae more acccurate predicitons

The variables chosen for this analysis include:

  1. income because -

acs_variable_list.2021 <- load_variables(2021, #year
                                         "acs5", #five year ACS estimates
                                         cache = TRUE)
# 2021, A

# Retrieve ACS data for Philadelphia tracts in 2020
tracts21 <- get_acs(
  geography = "tract",
  variables = c(
    "B01003_001",   # Total Population
    "B19013_001",   # Median Household Income
    "B25058_001",   # Median Rent
    "B25008_002",   # Owner-Occupied Units
    "B25008_003",   # Renter-Occupied Units
    "B07001_032",   # Same House 75 Years Ago
    "B07001_017",   # Same House 1 Year Ago
    "B25088_003",   # Median Selected Monthly Owner Costs (homes without a mortgage)
    "B25088_002",   # Median Selected Monthly Owner Costs (homes with a mortgage)
    "B25064_001",   # Median Gross Rent (rent and utilities)
    "B25117_001",   # Percentage of Housing Units with heat
    "B15003_022",   # Educational Attainment: Bachelor's Degree
    "B17001_002",   # Percentage of Population Below the Poverty Level
    "B28002_004",   # Percentage of Housing Units with High-Speed Internet
    "B25044_003",   # Percentage of Housing Units with No Vehicle Available
    "B02001_002",   # Race and Ethnicity: White Alone
    "B02001_003",   # Race and Ethnicity: Black or African American Alone
    "B03001_003"    # Hispanic or Latino Origin of Population
  ),
  year = 2021,
  state = "PA",
  county = "Philadelphia",
  geometry = TRUE,
  output = "wide"
)%>%
  select(-NAME, -ends_with("M")) %>%
  rename(totalpop = B01003_001E,                           # Total Population
         med_income = B19013_001E,                         # Median Household Income
         med_rent = B25058_001E,                           # Median Rent
         owner_units = B25008_002E,                        # Owner-Occupied Units
         renter_units = B25008_003E,                       # Renter-Occupied Units
         same_house_75 = B07001_032E,                      # Same House 75 Years Ago
         same_house_1 = B07001_017E,                       # Same House 1 Year Ago
         monthly_costs_no_mortgage = B25088_003E,          # Median Selected Monthly Owner Costs (homes without a mortgage)
         monthly_costs_with_mortgage = B25088_002E,        # Median Selected Monthly Owner Costs (homes with a mortgage)
         med_gross_rent = B25064_001E,                     # Median Gross Rent (rent and utilities)
         housing_units_with_heat = B25117_001E,            # Percentage of Housing Units with heat
         edu_bachelors = B15003_022E,                      # Educational Attainment: Bachelor's Degree
         pop_below_poverty = B17001_002E,                  # Percentage of Population Below the Poverty Level
         housing_units_high_speed_internet = B28002_004E,  # Percentage of Housing Units with High-Speed Internet
         housing_units_no_vehicle = B25044_003E,           # Percentage of Housing Units with No Vehicle Available
         race_white = B02001_002E,                         # Race and Ethnicity: White Alone
         race_black = B02001_003E,                         # Race and Ethnicity: Black or African American Alone
         hispanic_latino = B03001_003E                     # Race and Ethnicity: Hispanic or Latino
         )

# Transform the data to ESRI:102728 projection
tracts21 <- tracts21 %>% st_transform(st_crs(data))

Open Data philly

private schools proximity, parks and landmarks, floodplains, daily arrests, litter score, heat index

philly rising boundaries?

# Nearest Schools
## Adding data on Philadelphia Schools

PhillySchools <-
   st_read("./data/Schools.geojson") %>%
  st_transform(st_crs(tracts21))

PhillyPvtSchools <-
   st_read("./data/Schools.geojson") %>%
  filter(TYPE_SPECIFIC == "PRIVATE") %>%
  st_transform(st_crs(tracts21))

## Mapping nearest school

nearest_school <- sf::st_nearest_feature(data, PhillySchools)
nearest_pvt_school <- sf::st_nearest_feature(data, PhillyPvtSchools)

## Converting schools to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillySchools)

## Calculating distance

data$dist_to_nearest_school <- rsgeo::distance_euclidean_pairwise(x, y[nearest_school])

## Converting private schools to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPvtSchools)

## Calculating distance

data$dist_to_nearest_pvt_school <- rsgeo::distance_euclidean_pairwise(x, y[nearest_pvt_school])

# Nearest Colleges
## Adding data on Philadelphia Colleges

PhillyColleges <-
 st_read("./data/Universities_Colleges.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest college

nearest_college <- sf::st_nearest_feature(data, PhillyColleges)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyColleges)

## Calculating distance

data$dist_to_nearest_college <- rsgeo::distance_euclidean_pairwise(x, y[nearest_college])

# Nearest Landmarks
## Adding data on Philadelphia landmarks 

PhillyLandmarks <-
 st_read("https://services.arcgis.com/fLeGjb7u4uXqeF9q/arcgis/rest/services/Landmark_Points/FeatureServer/0/query?outFields=*&where=1%3D1&f=geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest landmark

nearest_landmark <- sf::st_nearest_feature(data, PhillyLandmarks)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyLandmarks)

## Calculating distance

data$dist_to_nearest_landmark <- rsgeo::distance_euclidean_pairwise(x, y[nearest_landmark])

# Nearest Commercial Corridors
## Adding data on Philadelphia Commercial Corridors 

PhillyComCorr <-
  st_read("./data/Commercial_Corridors.geojson") %>%
  st_transform(st_crs(tracts21))

## Is it within the commercial corridor?

data$within_com_corr <- ifelse(st_within(data, PhillyComCorr), 1, 0)

data <- data %>%
  mutate(within_com_corr = ifelse(is.na(within_com_corr), 0, 1))

## Mapping nearest commercial corridor

nearest_corridor <- sf::st_nearest_feature(data, PhillyComCorr)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyComCorr)

## Calculating distance

data$dist_to_comm_corr <- rsgeo::distance_euclidean_pairwise(x, y[nearest_corridor])

# Litter Index
## Adding data on Philadelphia's Litter Index

PhillyLitter <-
  st_read("./data/Litter_Index.geojson") %>%
  st_transform(st_crs(tracts21))

## Joining the litter score

data <- 
 st_join(data,(PhillyLitter %>%
          select(-OBJECTID, -Shape__Area, -Shape__Length )%>%
          rename(litter = SCORE))) 

#Nearest Flood Plains
## Adding data on Philadelphia's Flood Plain

PhillyFlood <- 
  st_read("./data/FEMA_100_flood_Plain.geojson") %>%
  st_transform(st_crs(tracts21))

## Is it within the floodplain?

data$within_flood <- ifelse(st_within(data, PhillyFlood), 1, 0)

data <- data %>%
  mutate(within_flood = ifelse(is.na(within_flood), 0, 1))

## Mapping nearest floodplain 

nearest_floodplain <- sf::st_nearest_feature(data, PhillyFlood)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyFlood)

## Calculating distance

data$dist_to_floodplain <- rsgeo::distance_euclidean_pairwise(x, y[nearest_floodplain])

# Nearest Transit Stops
## Adding data on Philadelphia's Transit Stops

el <- st_read("https://opendata.arcgis.com/datasets/8c6e2575c8ad46eb887e6bb35825e1a6_0.geojson")
Broad_St <- st_read("https://opendata.arcgis.com/datasets/2e9037fd5bef406488ffe5bb67d21312_0.geojson")

PhillySeptaStops <- 
  rbind(
     el %>% 
      mutate(Line = "El") %>%
      dplyr::select(Station, Line),
     Broad_St %>%
      mutate(Line ="Broad_St") %>%
      dplyr::select(Station, Line)) %>%
  st_transform(st_crs(tracts21))  

## Mapping nearest transit stop

nearest_station <- sf::st_nearest_feature(data, PhillySeptaStops)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillySeptaStops)

## Calculating distance

data$dist_to_nearest_station <- rsgeo::distance_euclidean_pairwise(x, y[nearest_station])

# Nearest Trails
## Adding data on Philadelphia Trails 

PhillyTrails <-
 st_read("./data/PPR_Trails.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest trail

nearest_trail <- sf::st_nearest_feature(data, PhillyTrails)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyTrails)

## Calculating distance

data$dist_to_nearest_trail <- rsgeo::distance_euclidean_pairwise(x, y[nearest_trail])

# Nearest Tennis Courts
## Adding data on Philadelphia Tennis Courts 

PhillyTennisCourts <-
 st_read("./data/PPR_Tennis_Courts.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest tennis court

nearest_tenniscourt <- sf::st_nearest_feature(data, PhillyTennisCourts)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyTennisCourts)

## Calculating distance

data$dist_to_nearest_tenniscourt <- rsgeo::distance_euclidean_pairwise(x, y[nearest_tenniscourt])

# Nearest Playgrounds
## Adding data on Philadelphia Playgrounds 

PhillyPlaygrounds <-
 st_read("./data/PPR_Playgrounds.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest playground

nearest_playground <- sf::st_nearest_feature(data, PhillyPlaygrounds)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPlaygrounds)

## Calculating distance

data$dist_to_nearest_playground <- rsgeo::distance_euclidean_pairwise(x, y[nearest_playground])

# Nearest Pools
## Adding data on Philadelphia Pools 

PhillyPools <-
 st_read("./data/PPR_Swimming_Pools.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest pool

nearest_pool <- sf::st_nearest_feature(data, PhillyPools)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPools)

## Calculating distance

data$dist_to_nearest_pool <- rsgeo::distance_euclidean_pairwise(x, y[nearest_pool])

# Nearest Police Stations
## Adding data on Philadelphia Police Stations

PhillyPoliceStations <-
 st_read("./data/Police_Stations.geojson")%>%
  st_transform(st_crs(tracts21))

## Mapping nearest police station

nearest_police_station <- sf::st_nearest_feature(data, PhillyPoliceStations)

## Converting to rsgeo geometries

x <- rsgeo::as_rsgeo(data)
y <- rsgeo::as_rsgeo(PhillyPoliceStations)

## Calculating distance

data$dist_to_nearest_police_station <- rsgeo::distance_euclidean_pairwise(x, y[nearest_police_station])
# Philadelphia Shootings
## Adding data on Philadelphia Shootings

PhillyShootings <-
 st_read("./data/shootings2021.geojson")%>%
  dplyr::select("date_", "point_x", "point_y", "geometry")%>%
  st_transform(st_crs(data))%>%
  na.omit() 
## Reading layer `shootings2021' from data source 
##   `D:\Fall_2023\PPA\Midterm\data\shootings2021.geojson' using driver `GeoJSON'
## replacing null geometries with empty geometries
## Simple feature collection with 2341 features and 21 fields (with 11 geometries empty)
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: -75.26654 ymin: 39.90286 xmax: -74.95936 ymax: 40.1173
## Geodetic CRS:  WGS 84
##calculating distance to nearest neighbors

library(sf)
library(spatstat)
library(class)

data_ppp <- as.ppp(data)
PhillyShootings_ppp <- as.ppp(PhillyShootings)

data$crime_nn5 <- nncross(data_ppp, PhillyShootings_ppp, k = 5)
tracts21 <- 
  tracts21 %>%
  mutate(PctWhite = ((race_white/totalpop)*100),
         PctBlack = ((race_black/totalpop)*100),
         PctHispanic = ((hispanic_latino/totalpop)*100),
         PctBachelors = ((edu_bachelors/totalpop)*100),
         PctPoverty = ((pop_below_poverty/totalpop)*100))
# joining census data

data <- 
  st_join(data, tracts21)
#saving for prediction 

data_og <- data

data <- data %>%
  filter(sale_price > 0) 

Exploratory Data Analysis

InternalVariables <- data 

InternalVariables <- st_drop_geometry(InternalVariables)

InternalVariables <- InternalVariables %>%
  dplyr::select("sale_price", "PricePerSqft", "total_livable_area", "year_built", "number_of_rooms", "number_of_bathrooms", "number_of_bedrooms") 

stargazer(as.data.frame(InternalVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Internal Variables (Figure 4.1)", out = "Training_PHLInternal.txt")
## 
## Descriptive Statistics for Philadelphia Homes Internal Variables (Figure 4.1)
## ===============================================================
## Statistic             N      Mean    St. Dev.   Min      Max   
## ---------------------------------------------------------------
## sale_price          18,040 282,857.8 228,937.6 11,000 5,900,000
## PricePerSqft        18,040   209.3     118.7    7.2    1,443.2 
## total_livable_area  18,040  1,334.0    516.8    186     7,391  
## year_built          18,040  1,937.8    29.8    1,750    2,024  
## number_of_rooms     18,040    6.1       0.7      3       14    
## number_of_bathrooms 18,040    1.2       0.4      1        5    
## number_of_bedrooms  18,040    2.9       0.6      1        8    
## ---------------------------------------------------------------
DemographicVariables <- data 

DemographicVariables <- st_drop_geometry(DemographicVariables)

DemographicVariables <- DemographicVariables %>%
  dplyr::select("PctWhite", "PctBlack", "PctHispanic", "PctBachelors", "PctPoverty", "med_income") 

stargazer(as.data.frame(DemographicVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Demographic Variables (Figure 4.1)", out = "Training_PHLSpatial.txt")
## 
## Descriptive Statistics for Philadelphia Homes Demographic Variables (Figure 4.1)
## ====================================================
## Statistic      N      Mean   St. Dev.  Min     Max  
## ----------------------------------------------------
## PctWhite     18,039   44.1     30.6    0.0    95.7  
## PctBlack     18,039   35.7     33.2    0.0    98.9  
## PctHispanic  18,039   15.4     18.8    0.0    91.7  
## PctBachelors 18,039   14.1     10.0    0.0    42.3  
## PctPoverty   18,039   20.4     13.1    0.0    65.1  
## med_income   17,882 61,320.0 28,995.2 11,955 210,322
## ----------------------------------------------------
SpatialVariables <- data 

SpatialVariables <- st_drop_geometry(SpatialVariables)

SpatialVariables <- SpatialVariables %>%
  dplyr::select("dist_to_nearest_school", "dist_to_nearest_pvt_school", "dist_to_nearest_landmark", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "dist_to_nearest_police_station","crime_nn5") 

stargazer(as.data.frame(SpatialVariables), type="text", digits=1, title = "Descriptive Statistics for Philadelphia Homes Spatial Variables (Figure 4.1)", out = "Training_PHLSpatial.txt")
## 
## Descriptive Statistics for Philadelphia Homes Spatial Variables (Figure 4.1)
## ====================================================================
## Statistic                        N     Mean   St. Dev. Min    Max   
## --------------------------------------------------------------------
## dist_to_nearest_school         18,040  351.4   202.0   9.9  1,799.4 
## dist_to_nearest_pvt_school     18,040  859.3   555.4   24.4 3,784.1 
## dist_to_nearest_landmark       18,040  441.6   259.8   5.8  2,213.8 
## dist_to_comm_corr              18,040  207.7   201.7   0.0  2,252.1 
## dist_to_nearest_station        18,040 2,683.8 2,809.9  35.9 13,808.6
## dist_to_nearest_trail          18,040 1,403.1  927.2   9.6  4,993.5 
## dist_to_nearest_police_station 18,040 1,652.1  959.3   29.4 5,378.6 
## --------------------------------------------------------------------
CategoricalVariables <- data 
CategoricalVariables <- st_drop_geometry(CategoricalVariables)
CategoricalVariables <- CategoricalVariables %>%
  dplyr::select("BasementType", "GarageType", "ViewType", "ExteriorConditionType", "InteriorConditionType", "building_code_description_new","sale_price") 

CategoricalVariables %>%
  gather(CategoricalVariables, Value, -sale_price) %>%
  ggplot(aes(Value, sale_price)) +
  geom_bar(stat = "identity", position = "dodge") +
  facet_wrap(~CategoricalVariables, scales = "free", ncol=2) +
  labs(title = "Price as a function of Categorical Variables") +
  plotTheme() + theme(axis.text.x = element_text(angle = 45, hjust = 1))

Mapping Internal Variables

##Mapping Internal Variables
# Mapping sale price

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\n Sale Price", 
                   na.value = NA) +
  labs(title="Properties by Sale Price", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.1.1") +
  mapTheme()

# Mapping Size

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(total_livable_area)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "total_livable_area"),
                   name="Quintile Breaks:\nArea in Sq Ft", 
                   na.value = NA) +
  labs(title="Properties by Size", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.1.2") +
  mapTheme()

# Mapping Interior Condition

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = (InteriorConditionType)), 
          show.legend = "point", size = .5) +
  scale_colour_manual(values = palettee, name="Interior Condition",
                   na.value = NA) +
  labs(title="Properties by Internal Condition", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.1.3") +
  mapTheme()

Mapping Spatial Variables

## Mapping Spatial Variables

# Mapping properties around schools

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75, alpha=0.3) + 
  geom_sf(data = PhillySchools, colour = "black", size = 1.5, alpha=0.6) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Properties Around Schools", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.2.1") +
  mapTheme()

# Mapping properties around commercial corridors

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75, alpha=0.3) + 
  geom_sf(data = PhillyComCorr, colour = "black", fill="black", alpha=0.6) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Properties Around Commercial Corridors", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.2.2") +
  mapTheme()

# Mapping properties around landmarks

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75, alpha=0.3) + 
  geom_sf(data = PhillyLandmarks, colour = "black", fill="black", size = .75, alpha=0.6) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Properties Around Landmarks", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.2.3") +
  mapTheme()

# Mapping properties around floodplains

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .75, alpha=0.3) + 
  geom_sf(data = PhillyFlood, colour = "black", fill="black", size = .75, alpha=0.6) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Properties Around Flood Plain", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.2.4") +
  mapTheme()

#library(gridExtra)

#grid.arrange(
  #b1,
  #b2,
  #b3,
  #b4,
  #nrow = 2,
  #widths=c(4,4),
  #top = "Title of the page"
  #)

Mapping Demographic Variables

## Mapping Demographic Variables

# Mapping median income around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(med_income)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "med_income"),
                      name = "Median Income\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="Median Income Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.1")+
  mapTheme()

# Mapping white population around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(PctWhite)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "PctWhite"),
                      name = "% White Population\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="% White Population Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.2")+
  mapTheme()

# Mapping black population around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(PctBlack)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "PctBlack"),
                      name = "% Black Population\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="% Black Population Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.3")+
  mapTheme()

# Mapping population with bachelor's degree around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(PctBachelors)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "PctBachelors"),
                      name = "% Bachelor's Degree\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="% Population with Bachelor's Degree Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.4")+
  mapTheme()

# Mapping population in poverty around sold properties

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "darkgrey") +
  geom_sf(data = tracts21, aes(fill = q5(PctPoverty)), color = "transparent", alpha=0.5) +
    scale_fill_brewer(palette = "BuPu",
                      labels = qBr(tracts21, "PctPoverty"),
                      name = "% Pop in Poverty\nQuintile Breaks") +
  geom_sf(data = data, aes(colour = q5(sale_price)), 
          show.legend = "point", size = .2) +
  scale_colour_manual(values = palettee,
                   labels=qBr(data, "sale_price"),
                   name="Quintile Breaks:\nSale Prices", 
                   na.value = NA) + 
  labs(title="% Population in Poverty Around Sold Properties", subtitle = "Philadelphia 2022-2023", 
      caption="Figure 3.3.5")+
  mapTheme()

Scatterplots

testing correlations

internalvariables <- c(
  "sale_price", "PricePerSqft", "total_livable_area", "number_of_rooms", "number_of_bathrooms", "number_of_bedrooms", "BuildingAge", "frontage", "depth", "fireplaces", "BasementPresent", "GaragePresent", "exterior_condition", "number_stories", "central_air"
)

numericintVars <- data %>%
  st_drop_geometry(data) %>%
  select(internalvariables)%>%
  na.omit()

ggcorrplot(
  round(cor(numericintVars), 1), 
  p.mat = cor_pmat(numericintVars),
  colors = c('#d7191c','#ffffbf','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across internal variables") 

demvariables <- c(
  "sale_price", "PctWhite", "PctBlack", "PctHispanic", "PctBachelors", "PctPoverty", "med_income",
  
  "med_rent", "owner_units",         "renter_units" 
        , "same_house_75" 
       ,  "same_house_1" 
      ,   "monthly_costs_no_mortgage" 
      ,   "monthly_costs_with_mortgage" 
        , "med_gross_rent"     
        , "housing_units_with_heat" 
        , "edu_bachelors" 
       ,  "pop_below_poverty" 
       ,  "housing_units_high_speed_internet" 
       ,  "housing_units_no_vehicle"       
  )

numericdemVars <- data %>%
  st_drop_geometry(data) %>%
  select(demvariables)%>%
  na.omit()

ggcorrplot(
  round(cor(numericdemVars), 1), 
  p.mat = cor_pmat(numericdemVars),
  colors = c('#d7191c','#ffffbf','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across Demographic variables") 

spatvariables <- c("sale_price", "dist_to_nearest_school", "dist_to_nearest_pvt_school", "dist_to_nearest_landmark", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "dist_to_nearest_police_station","crime_nn5", "litter", "within_com_corr", "within_flood", "dist_to_floodplain", "dist_to_nearest_tenniscourt", "dist_to_nearest_playground", "dist_to_nearest_pool", "total_livable_area"     
  )

numericspatVars <- data %>%
  st_drop_geometry(data) %>%
  select(all_of(spatvariables))%>%
  na.omit()

ggcorrplot(
  round(cor(numericspatVars), 1), 
  p.mat = cor_pmat(numericspatVars),
  colors = c('#d7191c','#ffffbf','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across Spatial variables") 

justify choice of varibales here

#variables <- c("sale_price", "number_of_bathrooms","dist_to_nearest_pvt_school", "dist_to_comm_corr", "dist_to_nearest_station", "dist_to_nearest_trail", "med_income", "dist_to_nearest_pool", "frontage", "central_air", "fireplaces", "BasementPresent", "exterior_condition", "PctWhite", "PctBlack", "total_livable_area", "BuildingAge",   "PctBachelors", "PctPoverty", "PricePerSqft","total_livable_area", "housing_units_with_heat", "PctHispanic", "housing_units_high_speed_nternet")

variables <- c ("sale_price" ,"total_livable_area","number_of_bathrooms", "fireplaces", "exterior_condition", "central_air", "PctWhite", "med_income", "monthly_costs_with_mortgage" , "PctPoverty", "dist_to_floodplain", "within_com_corr")

#dropping crime for now check later 

numericVars <- data %>%
  st_drop_geometry(data) %>%
  select(variables)%>%
  na.omit()

ggcorrplot(
  round(cor(numericVars), 1), 
  p.mat = cor_pmat(numericVars),
  colors = c('#d7191c','#ffffbf','#2c7bb6'),
  type="lower",
  insig = "blank") +  
    labs(title = "Correlation across numeric variables") 

Regressions

## Creating the training and test set
modelling_data <- data %>% filter(toPredict == "MODELLING")

set.seed(999) #makes sure data is split same way every time

## Splitting the data
split <- sample.split(modelling_data$objectid, SplitRatio = 0.75)

## Creating the training and test sets
train_data <- modelling_data[split,]
test_data <- modelling_data[!split,]
#regression on training data

reg2 <- lm(sale_price ~ ., data = st_drop_geometry(train_data) %>%  
                                 dplyr::select(variables))

## Plot regression 
effect_plot(reg2, pred = number_of_bathrooms, interval = TRUE, plot.points = TRUE)

plot_summs(reg2, scale = TRUE)

test_data <-
  test_data %>%
  mutate(Price.Predict = predict(reg2, test_data),
         Price.Error = Price.Predict - sale_price,
         Price.AbsError = abs(Price.Predict - sale_price),
         Price.APE = (abs(Price.Predict - sale_price)) / Price.Predict)
# table of MAE and MAPE

## Accuracy - Mean Absolute Error
MAE <- data.frame(mean(test_data$Price.AbsError, na.rm = T))
MAPE <- data.frame(mean(test_data$Price.APE, na.rm = T)) #MAPE


reg.MAE.MAPE <- cbind(MAE, MAPE) %>%
  kable(format = "html", caption = "Regression MAE & MAPE (Figure 5.1)", col.names = c("Mean Absolute Error", "Mean Absolute Percentage Error")) %>%
  kable_styling("striped", full_width = FALSE)

reg.MAE.MAPE
Regression MAE & MAPE (Figure 5.1)
Mean Absolute Error Mean Absolute Percentage Error
72986.7 0.2861265
test_data <-
  test_data %>%
  filter(sale_price < 10000000)

## K-Fold: Generalizability Cross-Validation

fitControl <- trainControl(method = "cv", number = 100)
set.seed(999)

reg.cv <- 
  train(sale_price ~ ., data = st_drop_geometry(test_data) %>% 
                                dplyr::select(variables),
method = "lm", trControl = fitControl, na.action = na.pass)

#This needs to be work-shopped. It stopped working and I don't know why. The code without an object assigned also outputs.

#reg.cv$resample[1:5,]

#mean(reg.cv$resample[,3])

#reg.cv$resample[7,]


stargazer(as.data.frame(reg.cv$resample), type="text", digits=1, title="Cross Validation Results (5.2)", out = "CV.txt") #all cv
## 
## Cross Validation Results (5.2)
## ===================================================
## Statistic  N    Mean    St. Dev.   Min       Max   
## ---------------------------------------------------
## RMSE      100 113,131.9 66,213.0 61,449.3 583,198.8
## Rsquared  100    0.7      0.1      0.3       0.9   
## MAE       100 71,080.9  15,814.0 44,469.5 155,013.9
## ---------------------------------------------------
#plotting the cross validation stuff

ggplot(reg.cv$resample, aes(x=MAE)) +
  geom_histogram(fill = "#2c7bb6") +
  labs(title = "Cross Validation Tests in Mean Average Error", caption="Figure 5.3") +
  plotTheme()

test_data %>%
  dplyr::select(Price.Predict, sale_price) %>%
    ggplot(aes(sale_price, Price.Predict)) +
  geom_point() +
  stat_smooth(aes(sale_price, sale_price), 
             method = "lm", se = FALSE, size = 1, colour="#d7191c") + 
  stat_smooth(aes(Price.Predict, sale_price), 
              method = "lm", se = FALSE, size = 1, colour="#2c7bb6") +
  labs(title="Predicted Sale Price as a Function of Observed Price",
       subtitle="Red line represents a perfect prediction; Blue line represents prediction",
       caption = "Figure 6.2",
       x = "Observed Price",
       y = "Predicted Price") +
  plotTheme()

#use the same thng for wights and morans i 
#move the filters to this cel too 


# Spatial Lag for price
coords <- st_coordinates(test_data) 
neighborList <- knn2nb(knearneigh(coords, 5))
spatialWeights <- nb2listw(neighborList, style="W")
test_data$lagPrice <- lag.listw(spatialWeights, test_data$sale_price)

test_data$lagPriceError <- lag.listw(spatialWeights, test_data$Price.Error, NAOK = TRUE)

# Filtering greater than 0 values

test_data_filter <- test_data %>%
  filter(Price.Error > 0, lagPriceError > 0)

ggplot(data = test_data_filter, aes(lagPriceError, Price.Error)) +
  geom_point(size = .85,colour = "black") + 
  geom_smooth(method = "lm",colour = "red",size = 1.2) +
  labs(title="Price Errors",
       caption = "Figure 6.1") +
  plotTheme()

# for morans 1

# Spatial Lag for price
coords <- st_coordinates(test_data_filter) 
neighborList <- knn2nb(knearneigh(coords, 5))
spatialWeights <- nb2listw(neighborList, style="W")
test_data_filter$lagPrice <- lag.listw(spatialWeights, test_data_filter$sale_price)

test_data_filter$lagPriceError <- lag.listw(spatialWeights, test_data_filter$Price.Error, NAOK = TRUE)



moranTest <- moran.mc(na.omit(test_data_filter$Price.Error),
                      spatialWeights, nsim = 999)  

ggplot(as.data.frame(moranTest$res[c(1:999)]), aes(moranTest$res[c(1:999)])) +
  geom_histogram(binwidth = 0.005) +
  geom_vline(aes(xintercept = moranTest$statistic), colour = "#d7191c",size=1) +
  scale_x_continuous(limits = c(-0.5,0.5)) +
  labs(title="Observed and permuted Moran's I",
       subtitle= "Observed Moran's I in red",
       caption = "Figure 6.3",
       x="Moran's I",
       y="Count") +
  plotTheme()

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "grey89") +
  geom_sf(data = test_data, aes(colour = q5(Price.AbsError)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(test_data,"Price.AbsError"),
                   name="Quintile\nBreaks") +
  labs(title="Map of Price Absolute Errors, Phill", 
      caption="Figure 6.4") +
  mapTheme()

ggplot() +
  geom_sf(data = tracts21, fill = "grey89", color = "grey89") +
  geom_sf(data = test_data, aes(colour = q5(Price.AbsError)), 
          show.legend = "point", size = .75) +
  scale_colour_manual(values = palettee,
                   labels=qBr(test_data,"Price.Predict"),
                   name="Quintile\nBreaks") +
  labs(title="predicted sale price", 
      caption="Figure 6.4") +
  mapTheme()

#this is wrong 

st_drop_geometry(test_data) %>%
  group_by(census_tract) %>%
  summarize(MAPE = mean(Price.APE, na.rm = T)) %>%
  ungroup() %>% 
  cross_join( tracts21)%>%
    st_sf() %>%
    ggplot() + 
   geom_sf(aes(fill = MAPE)) +
      scale_fill_gradient(low = palettee[5], high = palettee[1],
                          name = "MAPE") +
  geom_sf(data = test_data, colour = "black", show.legend = "point", size = .5) +
      labs(title = "Mean test set MAPE by Block Groups",
           caption = "Figure 7.3") +
      mapTheme()

test_data <-
  test_data %>%
  group_by(census_tract) %>%
  mutate(MeanPrice = mean(sale_price))

test_data <-
  test_data %>%
  group_by(census_tract) %>%
  mutate(MAPE = mean(Price.APE)) %>%
  filter(MAPE> -100)

ggplot(test_data) +
  geom_point(aes(MeanPrice, MAPE)) +
  geom_smooth(method = "lm", aes(MeanPrice, MAPE), se = FALSE, colour = "green") +
  labs(title = "MAPE by Block Group as a function of mean price by Block Group",
       x = "Mean Home Price", y = "MAPE",
       caption = "Figure 7.5") +
  plotTheme()

Prediction Challenge

#for prediction 

reg1 <- lm(sale_price ~ ., data = st_drop_geometry(data_og) %>%  
             dplyr::filter(toPredict == "MODELLING") %>%
                                 dplyr::select(variables))

data_pred <- st_drop_geometry(data_og)%>%
  filter(toPredict== "CHALLENGE")
  
data_pred <-
  data_pred %>%
  mutate(Price.Predict = predict(reg1,data_pred))

final_pred <- st_drop_geometry(data_pred)%>%
  select(musaID,Price.Predict)%>%
  mutate(team = "Sam and Roshini")

# pretty regression

print(stargazer(reg1, type="text", digits=1, title="Linear Model of Training Dataset (Figure 5)", out = "Training LM.txt"))
## 
## Linear Model of Training Dataset (Figure 5)
## =======================================================
##                                 Dependent variable:    
##                             ---------------------------
##                                     sale_price         
## -------------------------------------------------------
## total_livable_area                   175.7***          
##                                        (2.3)           
##                                                        
## number_of_bathrooms                 57,750.5***        
##                                      (2,841.4)         
##                                                        
## fireplaces                          34,169.9***        
##                                      (3,850.0)         
##                                                        
## exterior_condition                 -23,534.5***        
##                                      (1,321.8)         
##                                                        
## central_air                         25,448.7***        
##                                      (2,518.9)         
##                                                        
## PctWhite                             590.4***          
##                                       (50.1)           
##                                                        
## med_income                            0.4***           
##                                        (0.1)           
##                                                        
## monthly_costs_with_mortgage          144.6***          
##                                        (3.0)           
##                                                        
## PctPoverty                             34.0            
##                                       (118.1)          
##                                                        
## dist_to_floodplain                    -2.5**           
##                                        (1.3)           
##                                                        
## within_com_corr                     35,580.9***        
##                                      (4,070.1)         
##                                                        
## Constant                           -210,901.3***       
##                                      (9,479.3)         
##                                                        
## -------------------------------------------------------
## Observations                          17,741           
## R2                                      0.7            
## Adjusted R2                             0.7            
## Residual Std. Error           130,239.6 (df = 17729)   
## F Statistic                 3,386.7*** (df = 11; 17729)
## =======================================================
## Note:                       *p<0.1; **p<0.05; ***p<0.01
##  [1] ""                                                       
##  [2] "Linear Model of Training Dataset (Figure 5)"            
##  [3] "======================================================="
##  [4] "                                Dependent variable:    "
##  [5] "                            ---------------------------"
##  [6] "                                    sale_price         "
##  [7] "-------------------------------------------------------"
##  [8] "total_livable_area                   175.7***          "
##  [9] "                                       (2.3)           "
## [10] "                                                       "
## [11] "number_of_bathrooms                 57,750.5***        "
## [12] "                                     (2,841.4)         "
## [13] "                                                       "
## [14] "fireplaces                          34,169.9***        "
## [15] "                                     (3,850.0)         "
## [16] "                                                       "
## [17] "exterior_condition                 -23,534.5***        "
## [18] "                                     (1,321.8)         "
## [19] "                                                       "
## [20] "central_air                         25,448.7***        "
## [21] "                                     (2,518.9)         "
## [22] "                                                       "
## [23] "PctWhite                             590.4***          "
## [24] "                                      (50.1)           "
## [25] "                                                       "
## [26] "med_income                            0.4***           "
## [27] "                                       (0.1)           "
## [28] "                                                       "
## [29] "monthly_costs_with_mortgage          144.6***          "
## [30] "                                       (3.0)           "
## [31] "                                                       "
## [32] "PctPoverty                             34.0            "
## [33] "                                      (118.1)          "
## [34] "                                                       "
## [35] "dist_to_floodplain                    -2.5**           "
## [36] "                                       (1.3)           "
## [37] "                                                       "
## [38] "within_com_corr                     35,580.9***        "
## [39] "                                     (4,070.1)         "
## [40] "                                                       "
## [41] "Constant                           -210,901.3***       "
## [42] "                                     (9,479.3)         "
## [43] "                                                       "
## [44] "-------------------------------------------------------"
## [45] "Observations                          17,741           "
## [46] "R2                                      0.7            "
## [47] "Adjusted R2                             0.7            "
## [48] "Residual Std. Error           130,239.6 (df = 17729)   "
## [49] "F Statistic                 3,386.7*** (df = 11; 17729)"
## [50] "======================================================="
## [51] "Note:                       *p<0.1; **p<0.05; ***p<0.01"
LS0tDQp0aXRsZTogIlByZWRpY3RpbmcgSG91c2luZyBQcmljZXMgaW4gUGhpbGFkZWxwaGlhLCBQQSINCmF1dGhvcjogIlNhbXJpZGRoaSBLaGFyZSwgUm9zaGluaSBHYW5lc2giDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiBqb3VybmFsICANCi0tLQ0KDQojIEludHJvZHVjdGlvbg0KDQpUaGUgUGhpbGFkZWxwaGlhIGhvdXNpbmcgbWFya2V0IGlzIGEgZHluYW1pYyBhbmQgZGl2ZXJzZSBsYW5kc2NhcGUgaW5mbHVlbmNlZCBieSB2YXJpb3VzIGZhY3RvcnMgdGhhdCBpbXBhY3QgYm90aCBwcmljZXMgYW5kIG92ZXJhbGwgbWFya2V0IGNvbmRpdGlvbnMuIFRoaXMgbWFya2V0IHJlZmxlY3RzIHRoZSBlY29ub21pYyBhbmQgc29jaWFsIGR5bmFtaWNzIG9mIHRoZSBjaXR5IGFuZCB0aGUgc3Vycm91bmRpbmcgcmVnaW9uLkl0IGVuY29tcGFzc2VzIGEgd2lkZSByYW5nZSBvZiBuZWlnaGJvcmhvb2RzLCBlYWNoIHdpdGggaXRzIHVuaXF1ZSBjaGFyYWN0ZXIsIGFtZW5pdGllcywgYW5kIGhvdXNpbmcgc3R5bGVzLiBUaGUgY2l0eSdzIGRpdmVyc2UgaG91c2luZyBzdG9jayBpbmNsdWRlcyByb3dob3VzZXMsIHRvd25ob21lcywgYXBhcnRtZW50cywgYW5kIHNpbmdsZS1mYW1pbHkgaG9tZXMsIGNhdGVyaW5nIHRvIGEgdmFyaWV0eSBvZiBsaWZlc3R5bGVzIGFuZCBwcmVmZXJlbmNlcy4NCg0KSG91c2luZyBwcmljZXMgaW4gUGhpbGFkZWxwaGlhIGhhdmUgZXhwZXJpZW5jZWQgZmx1Y3R1YXRpb25zIG92ZXIgdGhlIHllYXJzLCBpbmZsdWVuY2VkIGJ5IGEgY29tYmluYXRpb24gb2YgbG9jYWwgYW5kIG5hdGlvbmFsIGZhY3RvcnMuIEhvd2V2ZXIsIGl0J3MgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB0aGVzZSBwcmljZXMgY2FuIHZhcnkgc2lnbmlmaWNhbnRseSBiYXNlZCBvbiBmYWN0b3JzIHN1Y2ggYXMgdGhlIG5laWdoYm9yaG9vZCwgcHJvcGVydHkgdHlwZSwgYW5kIGNvbmRpdGlvbiBvZiB0aGUgaG9tZS4gVGhpcyBwcm9qZWN0IGFpbXMgdG8gZXZhbHVhdGUgdGhlIHZhcmlvdXMgZmFjdG9ycyBhZmZlY3RpbmcgaG91c2luZyBwcmljZXMgYW5kIGNyZWF0ZSBhbiBhY2N1cmF0ZSBhbmQgZ2VuZXJhbGl6YWJsZSBPTFMgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IHRoZSBob3VzaW5nIHByaWNlcyBpbiBQaGlsYWRlbHBoaWEuDQoNCjxpbWcgc3JjPSIuL2ltYWdlcy8zM3JkLVN0cmVldC1Db3JuZXItb2YtV2VzdC1MZWhpZ2gtQXZlLmpwZyIgd2lkdGg9IjMwMCIgYWxpZ249InJpZ2h0IiBzdHlsZT0iZGlzcGxheTogaW5saW5lOyBtYXJnaW46IDAgMTBweDsiLz4NCg0KVGhlIHByb2Nlc3Mgb2YgY3JlYXRpbmcgdGhpcyBtb2RlbCB3YXMgZXh0cmVtZWx5IGl0ZXJhdGl2ZSwgYW5kIGludm9sdmVkIG11bHRpcGxlIHJvdW5kcyBvZiB0cmlhbCBhbmQgZXJyb3IsIHBhcnRpY3VsYXJseSBpbiBhdHRlbXB0cyB0byBtaW5pbWl6ZSB0aGUgZXJyb3JzIGluIHByZWRpY3Rpb25zLiBUaGUgbWFwcywgY29ycmVsYXRpb24gbWF0cml4IGFuZCBzY2F0dGVycGxvdHMgYXJlIGFsbCBpbGx1c3RyYXRpdmUgb2YgdmFyaWFibGVzIHRoYXQgd2VyZSBjb25zaWRlcmVkLCB3cmFuZ2xlZCBhbmQgZmlsdGVyZWQgYXMgcGFydCBvZiB0aGUgZGF0YSBhbmFseXNpcyBwcm9jZXNzLiAgDQoNClRoaXMgY29kZSBpcyBidWlsdCB1cG9uIHRoZSBjbGFzc3dvcmsgZGlzY3Vzc2VkIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vbWFmaWNobWFuL211c2FfNTA4MF8yMDIzL3RyZWUvbWFpbikuDQoNCmBgYHtyIFNldHVwIEtuaXR0aW5nIFBhcmFtZXRlcnMsIGluY2x1ZGU9RkFMU0V9DQogIGtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgICBlY2hvID0gVFJVRSwNCiAgICB3YXJuaW5nID0gRkFMU0UsDQogICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgb3V0LndpZHRoID0gJzEwMCUnLA0KICAgIGZpZy5yZXRpbmEgPTMNCiAgKQ0KYGBgDQoNCiMjIFIgU2V0dXAgYW5kIEluc3RhbGxpbmcgUGFja2FnZXMNCg0KVGhpcyBjb2RlIGNodW5rIGhhbmRsZXMgdGhlIGVzc2VudGlhbCB0YXNrcyBvZiBsb2FkaW5nIG5lY2Vzc2FyeSBwYWNrYWdlcywgY29uZmlndXJpbmcgdGhlIENlbnN1cyBBUEkga2V5LCBkZWZpbmluZyBjbGFzcyBmdW5jdGlvbnMsIHNwZWNpZnlpbmcgYSBjb2xvciBwYWxldHRlLCBhbmQgbWFuYWdpbmcgZ2xvYmFsIGVudmlyb25tZW50IHNldHRpbmdzLg0KDQpgYGB7ciBTZXQgdXAgUGFja2FnZXMsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KDQojIExvYWRpbmcgbGlicmFyaWVzDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0aWR5Y2Vuc3VzKQ0KbGlicmFyeShzZikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHRpZ3JpcykNCmxpYnJhcnkoZ2djb3JycGxvdCkNCmxpYnJhcnkoc3RhcmdhemVyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkoc3BkZXApDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShqdG9vbHMpICAgICAjIGZvciByZWdyZXNzaW9uIG1vZGVsIHBsb3RzDQpsaWJyYXJ5KGdnc3RhbmNlKSAjIHRvIHN1cHBvcnQganRvb2xzIHBsb3RzDQpsaWJyYXJ5KGdncHVicikgICAgIyBwbG90dGluZyBSXjIgdmFsdWUgb24gZ2dwbG90IHBvaW50IHNjYXR0ZXINCmxpYnJhcnkoYnJvb20ubWl4ZWQpICMgbmVlZGVkIGZvciBlZmZlY3RzIHBsb3RzDQoNCiMgU2V0dGluZyBwYXJhbWV0ZXJzIGZvciBzY2llbnRpZmljIG5vdGF0aW9uDQoNCm9wdGlvbnMoc2NpcGVuPTk5OSkNCm9wdGlvbnModGlncmlzX2NsYXNzID0gInNmIikNCg0KIyBGdW5jdGlvbnMgYW5kIGRhdGEgZGlyZWN0b3J5DQoNCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3VyYmFuU3BhdGlhbC9QdWJsaWMtUG9saWN5LUFuYWx5dGljcy1MYW5kaW5nL21hc3Rlci9mdW5jdGlvbnMuciIpDQoNCiMgSW52b2tpbmcgY29sb3IgcGFsZXR0ZXMgdG8gYmUgdXNlZA0KDQpwYWxldHRlZSA8LSBjKCcjZDcxOTFjJywnI2ZkYWU2MScsJyNmZmZmYmYnLCcjYWJkOWU5JywnIzJjN2JiNicpDQoNCiMgUmVnaXN0ZXJpbmcgQVBJIEtleSB0byBiZSB1c2VkDQoNCmNlbnN1c19hcGlfa2V5KCdiZjJkNTA3NjUxYjVhNjIxZGJhZGQ0NDUzM2ZiNGYzZGVhYWIyNmJmJywgb3ZlcndyaXRlID0gVFJVRSkNCg0KYGBgDQoNCiMgRGF0YSBXcmFuZ2xpbmcNCg0KRGF0YSBwcm92aWRlZCB3YXMgY2xlYW5lZCBhbmQgbmV3IHZhcmlhYmxlIHdlcmUgY3JlYXRlZCANCg0KYGBge3IgUmVhZGluZyBEYXRhLCByZXN1bHRzPSAnaGlkZSd9DQoNCiMgUmVhZGluZyBEYXRhDQoNCmRhdGEgPC0gDQogIHN0X3JlYWQoIi4vZGF0YS9zdHVkZW50RGF0YS5nZW9qc29uIikgJT4lDQogIHN0X3RyYW5zZm9ybSgnRVNSSToxMDIyODYnKQ0KDQojIERyb3BwaW5nIGNvbHVtbnMgd2l0aCBubyB2YWx1ZXMgYW5kIGZpbHRlcmluZyB2YWx1ZXMgd2l0aGluIFBoaWxhZGVscGhpYQ0KDQpkYXRhIDwtICBkYXRhICU+JSANCiAgc2VsZWN0KC1jcm9zc19yZWZlcmVuY2UsIC1kYXRlX2V4dGVyaW9yX2NvbmRpdGlvbiwgLW1haWxpbmdfYWRkcmVzc18yLCAtbWFpbGluZ19jYXJlX29mLCAtdW5maW5pc2hlZCwgLXV0aWxpdHksIC1jYXRlZ29yeV9jb2RlLCAtY2F0ZWdvcnlfY29kZV9kZXNjcmlwdGlvbiwgLWV4ZW1wdF9sYW5kLCAtc2VwYXJhdGVfdXRpbGl0aWVzLCAtc2V3ZXIsIC0gc2l0ZV90eXBlLCAtaG91c2VfZXh0ZW5zaW9uLCAtc3RyZWV0X2RpcmVjdGlvbiwgLXN1ZmZpeCwgLWdhcmFnZV90eXBlLCAtZ2VuZXJhbF9jb25zdHJ1Y3Rpb24gKSU+JSANCiAgZmlsdGVyKG1haWxpbmdfY2l0eV9zdGF0ZSA9PSAiUEhJTEFERUxQSElBIFBBIiApDQoNCmBgYA0KIyMgTWlzc2luZyBEYXRhIGFuZCBDYXRlZ29yaWNhbCBWYXJpYWJsZXMgDQoNCg0KYGBge3IgQ2xlYW5pbmcgRGF0YSBiYXNlZCBvbiBNZXRhZGF0YSwgcmVzdWx0cz0gJ2hpZGUnfQ0KDQojIyBGaWx0ZXJpbmcgb3V0IDkgcm93cyB3aGVyZSB5ZWFyIGJ1aWx0IGlzIG5vdCBnaXZlbg0KDQpkYXRhIDwtICBkYXRhICU+JSANCiAgZmlsdGVyKHllYXJfYnVpbHQgPiAwICkNCg0KIyMgQ2F0ZWdvcml6aW5nIGlmIGEgQmFzZW1lbnQgaXMgcHJlc2VudA0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShCYXNlbWVudFByZXNlbnQgPSBjYXNlX3doZW4oYmFzZW1lbnRzID09ICdBJyB8DQogIGJhc2VtZW50cyA9PSAnQicgfA0KICBiYXNlbWVudHMgPT0gJ0MnIHwNCiAgYmFzZW1lbnRzID09ICdEJyB8DQogIGJhc2VtZW50cyA9PSAnRScgfA0KICBiYXNlbWVudHMgPT0gJ0YnIHwNCiAgYmFzZW1lbnRzID09ICdHJyB8DQogIGJhc2VtZW50cyA9PSAnSCcgfA0KICBiYXNlbWVudHMgPT0gJ0knIHwNCiAgYmFzZW1lbnRzID09ICdKJyB+IDEsDQogIGJhc2VtZW50cyA9PSAnMCcgfiAwKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgb2YgMCB0byAnTkEnIHJvd3MgYmFzZWQgb24gZGVzY3JpcHRpb24gaW4gbWV0YWRhdGENCg0KZGF0YSRCYXNlbWVudFByZXNlbnRbaXMubmEoZGF0YSRCYXNlbWVudFByZXNlbnQpXSA8LSAwDQoNCiMjIENhdGVnb3JpemluZyBCYXNlbWVudCBUeXBlDQoNCmxpYnJhcnkoZHBseXIpDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEJhc2VtZW50VHlwZSA9IGNhc2Vfd2hlbihiYXNlbWVudHMgPT0gJ0EnIH4gJ0Z1bGwgc2l6ZSBmaW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnQicgfiAnRnVsbCBzaXplIHNlbWktZmluaXNoZWQnLA0KICBiYXNlbWVudHMgPT0gJ0MnIH4gJ0Z1bGwgc2l6ZSB1bmZpbmlzaGVkJywNCiAgYmFzZW1lbnRzID09ICdEJyB+ICdGdWxsIHNpemUgdW5rbm93biBmaW5pc2gnLA0KICBiYXNlbWVudHMgPT0gJ0UnIH4gJ1BhcnRpYWwgc2l6ZSBmaW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnRicgfiAnUGFydGlhbCBzaXplIHNlbWktZmluaXNoZWQnLA0KICBiYXNlbWVudHMgPT0gJ0cnIH4gJ1BhcnRpYWwgc2l6ZSB1bmZpbmlzaGVkJywNCiAgYmFzZW1lbnRzID09ICdIJyB+ICdQYXJ0aWFsIHNpemUgdW5rbm93biBmaW5pc2gnLA0KICBiYXNlbWVudHMgPT0gJ0knIH4gJ1Vua25vd24gc2l6ZSBmaW5pc2hlZCcsDQogIGJhc2VtZW50cyA9PSAnSicgfiAnVW5rbm93biBzaXplIHVuZmluaXNoZWQnLA0KICBiYXNlbWVudHMgPT0gJzAnIH4gJ05vIGJhc2VtZW50JykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIG9mIDAgdG8gJ05BJyByb3dzIGJhc2VkIG9uIGRlc2NyaXB0aW9uIGluIG1ldGFkYXRhDQoNCmRhdGEkQmFzZW1lbnRUeXBlW2lzLm5hKGRhdGEkQmFzZW1lbnRUeXBlKV0gPC0gIk5vIGJhc2VtZW50Ig0KZGF0YSRiYXNlbWVudHNbaXMubmEoZGF0YSRiYXNlbWVudHMpXSA8LSAwDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBDZW50cmFsIEFpcg0KDQpkYXRhJGNlbnRyYWxfYWlyIDwtIGlmZWxzZShkYXRhJGNlbnRyYWxfYWlyID09ICdZJywgMSwgMCkNCg0KIyMgQ2F0ZWdvcml6aW5nIGJhc2VkIG9uIEV4dGVyaW9yIENvbmRpdGlvbg0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShFeHRlcmlvckNvbmRpdGlvblR5cGUgPSBjYXNlX3doZW4oZXh0ZXJpb3JfY29uZGl0aW9uID09ICcxJyB8DQogIGV4dGVyaW9yX2NvbmRpdGlvbiA9PSAnMicgfA0KICBleHRlcmlvcl9jb25kaXRpb24gPT0gJzMnIH4gJ0dvb2QgQ29uZGl0aW9uJywNCiAgZXh0ZXJpb3JfY29uZGl0aW9uID09ICc0JyB8DQogIGV4dGVyaW9yX2NvbmRpdGlvbiA9PSAnNScgfiAnQXZlcmFnZSBDb25kaXRpb24nLA0KICBleHRlcmlvcl9jb25kaXRpb24gPT0gJzYnIH4gJ0JlbG93IEF2ZXJhZ2UgQ29uZGl0aW9uJywNCiAgZXh0ZXJpb3JfY29uZGl0aW9uID09ICc3JyB+ICdWYWNhbnQgYW5kIFNlYWxlZCcpKQ0KDQojIEFzc2lnbmluZyB2YWx1ZSB0byBzaW5nbGUgJ05BJyByb3cgYmFzZWQgb24gJ2ludGVyaW9yX2NvbnN0cnVjdGlvbicgcmF0aW5nIGZvciBzYW1lIHJvdw0KDQpkYXRhJEV4dGVyaW9yQ29uZGl0aW9uVHlwZVtpcy5uYShkYXRhJEV4dGVyaW9yQ29uZGl0aW9uVHlwZSldIDwtICJHb29kIENvbmRpdGlvbiINCmRhdGEkZXh0ZXJpb3JfY29uZGl0aW9uW2lzLm5hKGRhdGEkZXh0ZXJpb3JfY29uZGl0aW9uKV0gPC0gMw0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gRmlyZXBsYWNlcw0KDQojIEFzc2lnbmluZyB2YWx1ZSBvZiAwIHRvICdOQScgcm93cyBiYXNlZCBvbiBkZXNjcmlwdGlvbiBpbiBtZXRhZGF0YQ0KDQpkYXRhJGZpcmVwbGFjZXNbaXMubmEoZGF0YSRmaXJlcGxhY2VzKV0gPC0gMA0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gRnVlbCBTb3VyY2VzDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEZ1ZWxUeXBlID0gY2FzZV93aGVuKGZ1ZWwgPT0gJ0EnIH4gJ05hdHVyYWwgR2FzIFBvd2VyZWQnLA0KICBmdWVsID09ICdCJyB+ICdPaWwgUG93ZXJlZCcsDQogIGZ1ZWwgPT0gJ0MnIH4gJ0VsZWN0cmljIFBvd2VyZWQnLA0KICBmdWVsID09ICdFJyB+ICdTb2xhciBQb3dlcmVkJywNCiAgZnVlbCA9PSAnRycgfiAnRnVlbCBTb3VyY2UgVW5rbm93bicpKQ0KDQojIEFzc2lnbmluZyB2YWx1ZSBvZiBVbmtub3duL0cgdG8gJ05BJyByb3dzIGJhc2VkIG9uIGRlc2NyaXB0aW9uIGluIG1ldGFkYXRhDQoNCmRhdGEkRnVlbFR5cGVbaXMubmEoZGF0YSRGdWVsVHlwZSldIDwtICJGdWVsIFNvdXJjZSBVbmtub3duIg0KZGF0YSRmdWVsW2lzLm5hKGRhdGEkZnVlbCldIDwtICJHIg0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gR2FyYWdlIFByZXNlbmNlDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEdhcmFnZVByZXNlbnQgPSBjYXNlX3doZW4oZ2FyYWdlX3NwYWNlcyA9PSAnMCcgfiAwLA0KICBnYXJhZ2Vfc3BhY2VzID09ICcxJyB8DQogIGdhcmFnZV9zcGFjZXMgPT0gJzInIHwNCiAgZ2FyYWdlX3NwYWNlcyA9PSAnMycgfiAxKSkNCg0KIyBBc3NpZ25pbmcgdmFsdWUgb2YgMCB0byAnTkEnIHJvd3MgYmFzZWQgb24gZGVzY3JpcHRpb24gaW4gbWV0YWRhdGENCg0KZGF0YSRHYXJhZ2VQcmVzZW50W2lzLm5hKGRhdGEkR2FyYWdlUHJlc2VudCldIDwtIDANCmRhdGEkZ2FyYWdlX3NwYWNlc1tpcy5uYShkYXRhJGdhcmFnZV9zcGFjZXMpXSA8LSAwDQoNCiMjIENhdGVnb3JpemluZyBiYXNlZCBvbiBHYXJhZ2UgVHlwZXMNCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoR2FyYWdlVHlwZSA9IGNhc2Vfd2hlbihnYXJhZ2Vfc3BhY2VzID09ICcwJyB+ICdObyBHYXJhZ2UnLA0KICBnYXJhZ2Vfc3BhY2VzID09ICcxJyB+ICdTaW5nbGUgR2FyYWdlJywNCiAgZ2FyYWdlX3NwYWNlcyA9PSAnMicgfA0KICBnYXJhZ2Vfc3BhY2VzID09ICczJyB+ICdNdWx0aXBsZSBHYXJhZ2VzJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIG9mIDAgdG8gJ05BJyByb3dzIGJhc2VkIG9uIGRlc2NyaXB0aW9uIGluIG1ldGFkYXRhDQoNCmRhdGEkR2FyYWdlVHlwZVtpcy5uYShkYXRhJEdhcmFnZVR5cGUpXSA8LSAiTm8gR2FyYWdlIg0KZGF0YSRnYXJhZ2Vfc3BhY2VzW2lzLm5hKGRhdGEkZ2FyYWdlX3NwYWNlcyldIDwtIDANCg0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShJbnRlcmlvckNvbmRpdGlvblR5cGUgPSBjYXNlX3doZW4oIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnMCcgfA0KICBpbnRlcmlvcl9jb25kaXRpb24gPT0gJzEnIHwNCiAgaW50ZXJpb3JfY29uZGl0aW9uID09ICcyJyB8DQogIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnMycgfiAnR29vZCBDb25kaXRpb24nLA0KICBpbnRlcmlvcl9jb25kaXRpb24gPT0gJzQnIH4gJ0F2ZXJhZ2UgQ29uZGl0aW9uJywNCiAgaW50ZXJpb3JfY29uZGl0aW9uID09ICc1JyB+ICdCZWxvdyBBdmVyYWdlIENvbmRpdGlvbicsDQogIGludGVyaW9yX2NvbmRpdGlvbiA9PSAnNicgfA0KICBpbnRlcmlvcl9jb25kaXRpb24gPT0gJzcnIH4gJ1ZhY2FudCBhbmQvb3IgU2VhbGVkJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlcyB0byAwIG9yICdOQScgcm93cyBiYXNlZCBvbiBleHRlcmlvciBjb25kaXRpb24gc2luY2UgaW50ZXJpb3IgYW5kIGV4dGVyaW9yIGNvbmRpdGlvbnMgYXJlIGVxdWFsIGluIGFsbW9zdCBhbGwgY2FzZXMNCg0KZGF0YSRJbnRlcmlvckNvbmRpdGlvblR5cGVbaXMubmEoZGF0YSRJbnRlcmlvckNvbmRpdGlvblR5cGUpXSA8LSBjKGRhdGEkRXh0ZXJpb3JDb25kaXRpb25UeXBlKQ0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gVmlldyBUeXBlDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKFZpZXdUeXBlID0gY2FzZV93aGVuKHZpZXdfdHlwZSA9PSAnMCcgfiAnTmF0dXJlIG9mIFZpZXcgVW5rbm93bicsDQogIHZpZXdfdHlwZSA9PSAnSScgfiAnVHlwaWNhbCBWaWV3JywNCiAgdmlld190eXBlID09ICdBJyB+ICdTa3lsaW5lIFZpZXcnLA0KICB2aWV3X3R5cGUgPT0gJ0InIH4gJ1JpdmVyIFZpZXcnLA0KICB2aWV3X3R5cGUgPT0gJ0MnIH4gJ1BhcmsgVmlldycsDQogIHZpZXdfdHlwZSA9PSAnRCcgfiAnQ29tbWVyY2lhbCBBcmVhIFZpZXcnLA0KICB2aWV3X3R5cGUgPT0gJ0UnIH4gJ0luZHVzdHJpYWwgQXJlYSBWaWV3JywNCiAgdmlld190eXBlID09ICdIJyB+ICdWaWV3IG9mIExhbmRtYXJrJykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIHRvIE5BIHJvd3MgdG8gbmF0dXJlIG9mIHZpZXcgdW5rbm93bg0KDQpkYXRhJFZpZXdUeXBlW2lzLm5hKGRhdGEkVmlld1R5cGUpXSA8LSAiTmF0dXJlIG9mIFZpZXcgVW5rbm93biINCmRhdGEkdmlld190eXBlW2lzLm5hKGRhdGEkdmlld190eXBlKV0gPC0gMA0KDQojIyBDYXRlZ29yaXppbmcgYmFzZWQgb24gVG9wb2dyYXBoeSBUeXBlDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKFRvcG9ncmFwaHlUeXBlID0gY2FzZV93aGVuKHRvcG9ncmFwaHkgPT0gJ0EnIH4gJ0Fib3ZlIFN0cmVldCBMZXZlbCBUb3BvZ3JhcGh5JywNCiAgdG9wb2dyYXBoeSA9PSAnQicgfiAnQmVsb3cgU3RyZWV0IExldmVsIFRvcG9ncmFwaHknLA0KICB0b3BvZ3JhcGh5ID09ICdDJyB+ICdGbG9vZCBQbGFpbiBUb3BvZ3JhcGh5JywNCiAgdG9wb2dyYXBoeSA9PSAnRCcgfiAnUm9ja3kgVG9wb2dyYXBoeScsDQogIHRvcG9ncmFwaHkgPT0gJ0UnIH4gJ090aGVyIFRvcG9ncmFwaHknLA0KICB0b3BvZ3JhcGh5ID09ICdGJyB+ICdMZXZlbCBUb3BvZ3JhcGh5JykpDQoNCiMgQXNzaWduaW5nIHZhbHVlIHRvIE5BIHJvd3MgdG8gbmF0dXJlIG9mIHZpZXcgdW5rbm93bg0KDQpkYXRhJFRvcG9ncmFwaHlUeXBlW2lzLm5hKGRhdGEkVG9wb2dyYXBoeVR5cGUpXSA8LSAiVG9wb2dyYXBoeSBVbmtub3duIg0KZGF0YSR0b3BvZ3JhcGh5W2lzLm5hKGRhdGEkdG9wb2dyYXBoeSldIDwtIDANCg0KIyMgQ2F0ZWdvcml6aW5nIGJhc2VkIG9uIFBhcmNlbCBUeXBlDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKFBhcmNlbFR5cGUgPSBjYXNlX3doZW4ocGFyY2VsX3NoYXBlID09ICdBJyB+ICdJcnJlZ3VsYXIgUGFyY2VsJywNCiAgcGFyY2VsX3NoYXBlID09ICdCJyB+ICdHcm9zc2x5IElycmVndWxhciBQYXJjZWwnLA0KICBwYXJjZWxfc2hhcGUgPT0gJ0MnIH4gJ1RyaWFuZ3VsYXIgUGFyY2VsJywNCiAgcGFyY2VsX3NoYXBlID09ICdEJyB+ICdMb25nIE5hcnJvdyBQYXJjZWwnLA0KICBwYXJjZWxfc2hhcGUgPT0gJ0UnIH4gJ1JlY3Rhbmd1bGFyIFBhcmNlbCcpKQ0KDQojIEFzc2lnbmluZyB2YWx1ZSB0byBOQSByb3dzIHRvIG5hdHVyZSBvZiB2aWV3IHVua25vd24NCg0KZGF0YSRQYXJjZWxUeXBlW2lzLm5hKGRhdGEkUGFyY2VsVHlwZSldIDwtICJQYXJjZWwgVHlwZSBVbmtub3duIg0KZGF0YSRwYXJjZWxfc2hhcGVbaXMubmEoZGF0YSRwYXJjZWxfc2hhcGUpXSA8LSAwDQoNCg0KYGBgDQoNCmBgYHtyIEltcHV0aW5nIHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIGJlZHJvb21zLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KIyMgSW1wdXRpbmcgdmFsdWVzIGZvciBtaXNzaW5nIHZhbHVlcyBvZiBudW1iZXIgb2YgYmVkcm9vbXMgYmFzZWQgb24gdG90YWwgbGl2YWJsZSBhcmVhDQoNCiMgRHJvcHBpbmcgMzIgdmFsdWVzIG9mIHRvdGFsIGxpdmFibGUgYXJlYSB3aGljaCBhcmUgMCBmb3IgYmV0dGVyIHByZWRpY3Rpb24NCg0KZGF0YSA8LSAgZGF0YSAlPiUgDQogIGZpbHRlcih0b3RhbF9saXZhYmxlX2FyZWEgPiAwICkNCg0KIyBTdGVwIDEgLSBDcmVhdGluZyBhbiBpbmRleCBvZiAwIGFuZCAxIHZhbHVlcyBmb3Igcm93cyB0aGF0IGhhdmUgdmFsdWVzIGZvciBudW1iZXIgb2YgYmF0aHJvb21zIGFuZCByb3dzIHRoYXQgZG8gbm90DQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEJlZHJvb21JbmRleCA9IGNhc2Vfd2hlbihudW1iZXJfb2ZfYmVkcm9vbXMgPj0gMSB+IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyX29mX2JlZHJvb21zIDwgMSB+IDApKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KZGF0YSRCZWRyb29tSW5kZXhbaXMubmEoZGF0YSRCZWRyb29tSW5kZXgpXSA8LSAwDQoNCiMgU3RlcCAyIC0gQ3JlYXRpbmcgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCByZWxhdGluZyBudW1iZXIgb2YgYmVkcm9vbXMgYW5kIHRvdGFsIGxpdmVhYmxlIGFyZWENCg0KbG0obnVtYmVyX29mX2JlZHJvb21zIH4gdG90YWxfbGl2YWJsZV9hcmVhLCBkYXRhPWRhdGEpDQoNCiMgU3RlcCAzIC0gSW1wdXRpbmcgbmV3IHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIGJlZHJvb21zIHVzaW5nIHJlZ3Jlc3Npb24gcmVzdWx0cw0KDQpmb3IoaSBpbiAxOm5yb3coZGF0YSkpDQp7DQogIGlmIChkYXRhJEJlZHJvb21JbmRleFtpXSA9PSAwKQ0KICB7DQogICAgZGF0YSRudW1iZXJfb2ZfYmVkcm9vbXNbaV0gPSAyLjE2MDU2NTAgKyAwLjAwMDMwNTcqZGF0YSR0b3RhbF9saXZhYmxlX2FyZWFbaV0NCiAgfQ0KICB9DQoNCmRhdGEkbnVtYmVyX29mX2JlZHJvb21zIDwtIHJvdW5kKGRhdGEkbnVtYmVyX29mX2JlZHJvb21zLCBkaWdpdHM9MCkNCg0KYGBgDQoNCmBgYHtyIEltcHV0aW5nIHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIHJvb21zLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KIyMgSW1wdXRpbmcgdmFsdWVzIGZvciBtaXNzaW5nIHZhbHVlcyBvZiBudW1iZXIgb2Ygcm9vbXMgYmFzZWQgb24gdG90YWwgbGl2YWJsZSBhcmVhDQoNCiMgU3RlcCAxIC0gQ3JlYXRpbmcgYW4gaW5kZXggb2YgMCBhbmQgMSB2YWx1ZXMgZm9yIHJvd3MgdGhhdCBoYXZlIHZhbHVlcyBmb3IgbnVtYmVyIG9mIHJvb21zIGFuZCByb3dzIHRoYXQgZG8gbm90DQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKFJvb21JbmRleCA9IGNhc2Vfd2hlbihudW1iZXJfb2Zfcm9vbXMgPj0gMSB+IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyX29mX3Jvb21zIDwgMSB+IDApKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KZGF0YSRSb29tSW5kZXhbaXMubmEoZGF0YSRSb29tSW5kZXgpXSA8LSAwDQoNCiMgU3RlcCAyIC0gQ3JlYXRpbmcgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCByZWxhdGluZyBudW1iZXIgb2Ygcm9vbXMgYW5kIHRvdGFsIGxpdmFibGUgYXJlYQ0KDQpsbShudW1iZXJfb2Zfcm9vbXMgfiB0b3RhbF9saXZhYmxlX2FyZWEsIGRhdGE9ZGF0YSkNCg0KIyBTdGVwIDMgLSBJbXB1dGluZyBuZXcgdmFsdWVzIGZvciBtaXNzaW5nIHZhbHVlcyBvZiBudW1iZXIgb2Ygcm9vbXMgdXNpbmcgcmVncmVzc2lvbiByZXN1bHRzDQoNCmZvcihpIGluIDE6bnJvdyhkYXRhKSkNCnsNCiAgaWYgKGRhdGEkUm9vbUluZGV4W2ldID09IDApDQogIHsNCiAgICBkYXRhJG51bWJlcl9vZl9yb29tc1tpXSA9IDQuMzE2NTAzICsgMC4wMDEzMTkqZGF0YSR0b3RhbF9saXZhYmxlX2FyZWFbaV0NCiAgfQ0KICB9DQoNCmRhdGEkbnVtYmVyX29mX3Jvb21zIDwtIHJvdW5kKGRhdGEkbnVtYmVyX29mX3Jvb21zLCBkaWdpdHM9MCkNCg0KYGBgDQoNCmBgYHtyIEltcHV0aW5nIHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIGJhdGhyb29tcywgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCiMjIEltcHV0aW5nIHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIGJhdGhyb29tcyBiYXNlZCBvbiB0b3RhbCBsaXZhYmxlIGFyZWENCg0KIyBTdGVwIDEgLSBDcmVhdGluZyBhbiBpbmRleCBvZiAwIGFuZCAxIHZhbHVlcyBmb3Igcm93cyB0aGF0IGhhdmUgdmFsdWVzIGZvciBudW1iZXIgb2YgYmF0aHJvb21zIGFuZCByb3dzIHRoYXQgZG8gbm90DQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEJhdGhyb29tSW5kZXggPSBjYXNlX3doZW4obnVtYmVyX29mX2JhdGhyb29tcyA+PSAxIH4gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXJfb2ZfYmF0aHJvb21zIDwgMSB+IDApKQ0KDQpkYXRhJEJhdGhyb29tSW5kZXhbaXMubmEoZGF0YSRCYXRocm9vbUluZGV4KV0gPC0gMA0KDQojIFN0ZXAgMiAtIENyZWF0aW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgcmVsYXRpbmcgbnVtYmVyIG9mIGJhdGhyb29tcyBhbmQgIHRvdGFsIGxpdmFibGUgYXJlYQ0KDQpsbShudW1iZXJfb2ZfYmF0aHJvb21zIH4gdG90YWxfbGl2YWJsZV9hcmVhLCBkYXRhPWRhdGEpDQoNCiMgU3RlcCAzIC0gSW1wdXRpbmcgbmV3IHZhbHVlcyBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgbnVtYmVyIG9mIGJhdGhyb29tcyB1c2luZyByZWdyZXNzaW9uIHJlc3VsdHMNCg0KZm9yKGkgaW4gMTpucm93KGRhdGEpKQ0Kew0KICBpZiAoZGF0YSRCYXRocm9vbUluZGV4W2ldID09IDApDQogIHsNCiAgICBkYXRhJG51bWJlcl9vZl9iYXRocm9vbXNbaV0gPSAwLjY0MjYzMTAgKyAwLjAwMDMyODMqZGF0YSR0b3RhbF9saXZhYmxlX2FyZWFbaV0NCiAgfQ0KICB9DQoNCmRhdGEkbnVtYmVyX29mX2JhdGhyb29tcyA8LSByb3VuZChkYXRhJG51bWJlcl9vZl9iYXRocm9vbXMsIGRpZ2l0cz0wKQ0KDQpgYGANCg0KYGBge3IgUHJpY2UvU3FmdGEsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KDQpkYXRhIDwtIA0KICBkYXRhICU+JQ0KICBtdXRhdGUoUHJpY2VQZXJTcWZ0ID0gKHNhbGVfcHJpY2UvdG90YWxfbGl2YWJsZV9hcmVhKSkNCg0KZGF0YSA8LSANCiAgZGF0YSAlPiUNCiAgbXV0YXRlKEJ1aWxkaW5nQWdlID0gKDIwMjMgLSAoeWVhcl9idWlsdCkpKQ0KDQpgYGANCg0KDQpgYGB7ciBmaWx0ZXIgZGF0YSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCg0KZGF0YSA8LSBkYXRhICU+JSANCiAgc2VsZWN0KG9iamVjdGlkLCBhc3Nlc3NtZW50X2RhdGUsIEJ1aWxkaW5nQWdlLCB5ZWFyX2J1aWx0LCBidWlsZGluZ19jb2RlLCBidWlsZGluZ19jb2RlX2Rlc2NyaXB0aW9uLCBwaW4sIGJ1aWxkaW5nX2NvZGVfbmV3LCBidWlsZGluZ19jb2RlX2Rlc2NyaXB0aW9uX25ldywgIGNlbnN1c190cmFjdCwgZ2VvZ3JhcGhpY193YXJkLCB6b25pbmcsIGxvY2F0aW9uLCBzdHJlZXRfbmFtZSwgc3RyZWV0X2NvZGUsIHN0cmVldF9kZXNpZ25hdGlvbiwgemlwX2NvZGUsIGhvdXNlX251bWJlciwgZGVwdGgsIGZyb250YWdlLCBjZW50cmFsX2FpciwgZmlyZXBsYWNlcywgZnVlbCwgRnVlbFR5cGUsIGJhc2VtZW50cywgQmFzZW1lbnRQcmVzZW50LCBCYXNlbWVudFR5cGUsIGdhcmFnZV9zcGFjZXMsIEdhcmFnZVByZXNlbnQsIEdhcmFnZVR5cGUsIGV4dGVyaW9yX2NvbmRpdGlvbiwgRXh0ZXJpb3JDb25kaXRpb25UeXBlLCBpbnRlcmlvcl9jb25kaXRpb24sIEludGVyaW9yQ29uZGl0aW9uVHlwZSwgbnVtYmVyX29mX2JhdGhyb29tcywgbnVtYmVyX29mX2JlZHJvb21zLCBudW1iZXJfb2Zfcm9vbXMsIG51bWJlcl9zdG9yaWVzLCB0b3RhbF9saXZhYmxlX2FyZWEsIHZpZXdfdHlwZSwgVmlld1R5cGUsIHRvcG9ncmFwaHksIFRvcG9ncmFwaHlUeXBlLCBwYXJjZWxfc2hhcGUsIFBhcmNlbFR5cGUsIHNhbGVfZGF0ZSwgc2FsZV95ZWFyLCBzYWxlX3ByaWNlLCBQcmljZVBlclNxZnQsIG11c2FJRCwgdG9QcmVkaWN0LCBnZW9tZXRyeSApDQpgYGANCg0KYGBge3IgZmlsdGVyIG91dGxpZXJzLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KZGF0YSA8LSAgZGF0YSAlPiUgDQogIGZpbHRlcihudW1iZXJfb2ZfYmVkcm9vbXMgPDEwLCBudW1iZXJfb2Zfcm9vbXM8MTUsICgobnVtYmVyX29mX2JhdGhyb29tcytudW1iZXJfb2ZfYmVkcm9vbXMpIDwgbnVtYmVyX29mX3Jvb21zKSwgUHJpY2VQZXJTcWZ0PDE1MDAsIHRvdGFsX2xpdmFibGVfYXJlYTwxMDAwMCkNCg0KYGBgDQoNCg0KIyMgQ2Vuc3VzIERhdGENCg0KVGhlIHllYXJzIGNob3NlbiBmb3IgYW5hbHlzaXMgYXJlIDIwMjEgYmVjYXVzZSBjb3ZpZCByZWNvdmVyeSBtb3N0IHJlY2VudCB0byBta2FlIG1vcmUgYWNjY3VyYXRlIHByZWRpY2l0b25zDQoNCg0KVGhlIHZhcmlhYmxlcyBjaG9zZW4gZm9yIHRoaXMgYW5hbHlzaXMgaW5jbHVkZTogDQoNCjEuIGluY29tZSBiZWNhdXNlIC0gDQoNCjIuIA0KDQozLiANCg0KDQpgYGB7ciBjZW5zdXMsIGNhY2hlPVRSVUUsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9ICdoaWRlJ30NCg0KYWNzX3ZhcmlhYmxlX2xpc3QuMjAyMSA8LSBsb2FkX3ZhcmlhYmxlcygyMDIxLCAjeWVhcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYWNzNSIsICNmaXZlIHllYXIgQUNTIGVzdGltYXRlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYWNoZSA9IFRSVUUpDQojIDIwMjEsIEENCg0KIyBSZXRyaWV2ZSBBQ1MgZGF0YSBmb3IgUGhpbGFkZWxwaGlhIHRyYWN0cyBpbiAyMDIwDQp0cmFjdHMyMSA8LSBnZXRfYWNzKA0KICBnZW9ncmFwaHkgPSAidHJhY3QiLA0KICB2YXJpYWJsZXMgPSBjKA0KICAgICJCMDEwMDNfMDAxIiwgICAjIFRvdGFsIFBvcHVsYXRpb24NCiAgICAiQjE5MDEzXzAwMSIsICAgIyBNZWRpYW4gSG91c2Vob2xkIEluY29tZQ0KICAgICJCMjUwNThfMDAxIiwgICAjIE1lZGlhbiBSZW50DQogICAgIkIyNTAwOF8wMDIiLCAgICMgT3duZXItT2NjdXBpZWQgVW5pdHMNCiAgICAiQjI1MDA4XzAwMyIsICAgIyBSZW50ZXItT2NjdXBpZWQgVW5pdHMNCiAgICAiQjA3MDAxXzAzMiIsICAgIyBTYW1lIEhvdXNlIDc1IFllYXJzIEFnbw0KICAgICJCMDcwMDFfMDE3IiwgICAjIFNhbWUgSG91c2UgMSBZZWFyIEFnbw0KICAgICJCMjUwODhfMDAzIiwgICAjIE1lZGlhbiBTZWxlY3RlZCBNb250aGx5IE93bmVyIENvc3RzIChob21lcyB3aXRob3V0IGEgbW9ydGdhZ2UpDQogICAgIkIyNTA4OF8wMDIiLCAgICMgTWVkaWFuIFNlbGVjdGVkIE1vbnRobHkgT3duZXIgQ29zdHMgKGhvbWVzIHdpdGggYSBtb3J0Z2FnZSkNCiAgICAiQjI1MDY0XzAwMSIsICAgIyBNZWRpYW4gR3Jvc3MgUmVudCAocmVudCBhbmQgdXRpbGl0aWVzKQ0KICAgICJCMjUxMTdfMDAxIiwgICAjIFBlcmNlbnRhZ2Ugb2YgSG91c2luZyBVbml0cyB3aXRoIGhlYXQNCiAgICAiQjE1MDAzXzAyMiIsICAgIyBFZHVjYXRpb25hbCBBdHRhaW5tZW50OiBCYWNoZWxvcidzIERlZ3JlZQ0KICAgICJCMTcwMDFfMDAyIiwgICAjIFBlcmNlbnRhZ2Ugb2YgUG9wdWxhdGlvbiBCZWxvdyB0aGUgUG92ZXJ0eSBMZXZlbA0KICAgICJCMjgwMDJfMDA0IiwgICAjIFBlcmNlbnRhZ2Ugb2YgSG91c2luZyBVbml0cyB3aXRoIEhpZ2gtU3BlZWQgSW50ZXJuZXQNCiAgICAiQjI1MDQ0XzAwMyIsICAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBObyBWZWhpY2xlIEF2YWlsYWJsZQ0KICAgICJCMDIwMDFfMDAyIiwgICAjIFJhY2UgYW5kIEV0aG5pY2l0eTogV2hpdGUgQWxvbmUNCiAgICAiQjAyMDAxXzAwMyIsICAgIyBSYWNlIGFuZCBFdGhuaWNpdHk6IEJsYWNrIG9yIEFmcmljYW4gQW1lcmljYW4gQWxvbmUNCiAgICAiQjAzMDAxXzAwMyIgICAgIyBIaXNwYW5pYyBvciBMYXRpbm8gT3JpZ2luIG9mIFBvcHVsYXRpb24NCiAgKSwNCiAgeWVhciA9IDIwMjEsDQogIHN0YXRlID0gIlBBIiwNCiAgY291bnR5ID0gIlBoaWxhZGVscGhpYSIsDQogIGdlb21ldHJ5ID0gVFJVRSwNCiAgb3V0cHV0ID0gIndpZGUiDQopJT4lDQogIHNlbGVjdCgtTkFNRSwgLWVuZHNfd2l0aCgiTSIpKSAlPiUNCiAgcmVuYW1lKHRvdGFscG9wID0gQjAxMDAzXzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUb3RhbCBQb3B1bGF0aW9uDQogICAgICAgICBtZWRfaW5jb21lID0gQjE5MDEzXzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVkaWFuIEhvdXNlaG9sZCBJbmNvbWUNCiAgICAgICAgIG1lZF9yZW50ID0gQjI1MDU4XzAwMUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZWRpYW4gUmVudA0KICAgICAgICAgb3duZXJfdW5pdHMgPSBCMjUwMDhfMDAyRSwgICAgICAgICAgICAgICAgICAgICAgICAjIE93bmVyLU9jY3VwaWVkIFVuaXRzDQogICAgICAgICByZW50ZXJfdW5pdHMgPSBCMjUwMDhfMDAzRSwgICAgICAgICAgICAgICAgICAgICAgICMgUmVudGVyLU9jY3VwaWVkIFVuaXRzDQogICAgICAgICBzYW1lX2hvdXNlXzc1ID0gQjA3MDAxXzAzMkUsICAgICAgICAgICAgICAgICAgICAgICMgU2FtZSBIb3VzZSA3NSBZZWFycyBBZ28NCiAgICAgICAgIHNhbWVfaG91c2VfMSA9IEIwNzAwMV8wMTdFLCAgICAgICAgICAgICAgICAgICAgICAgIyBTYW1lIEhvdXNlIDEgWWVhciBBZ28NCiAgICAgICAgIG1vbnRobHlfY29zdHNfbm9fbW9ydGdhZ2UgPSBCMjUwODhfMDAzRSwgICAgICAgICAgIyBNZWRpYW4gU2VsZWN0ZWQgTW9udGhseSBPd25lciBDb3N0cyAoaG9tZXMgd2l0aG91dCBhIG1vcnRnYWdlKQ0KICAgICAgICAgbW9udGhseV9jb3N0c193aXRoX21vcnRnYWdlID0gQjI1MDg4XzAwMkUsICAgICAgICAjIE1lZGlhbiBTZWxlY3RlZCBNb250aGx5IE93bmVyIENvc3RzIChob21lcyB3aXRoIGEgbW9ydGdhZ2UpDQogICAgICAgICBtZWRfZ3Jvc3NfcmVudCA9IEIyNTA2NF8wMDFFLCAgICAgICAgICAgICAgICAgICAgICMgTWVkaWFuIEdyb3NzIFJlbnQgKHJlbnQgYW5kIHV0aWxpdGllcykNCiAgICAgICAgIGhvdXNpbmdfdW5pdHNfd2l0aF9oZWF0ID0gQjI1MTE3XzAwMUUsICAgICAgICAgICAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBoZWF0DQogICAgICAgICBlZHVfYmFjaGVsb3JzID0gQjE1MDAzXzAyMkUsICAgICAgICAgICAgICAgICAgICAgICMgRWR1Y2F0aW9uYWwgQXR0YWlubWVudDogQmFjaGVsb3IncyBEZWdyZWUNCiAgICAgICAgIHBvcF9iZWxvd19wb3ZlcnR5ID0gQjE3MDAxXzAwMkUsICAgICAgICAgICAgICAgICAgIyBQZXJjZW50YWdlIG9mIFBvcHVsYXRpb24gQmVsb3cgdGhlIFBvdmVydHkgTGV2ZWwNCiAgICAgICAgIGhvdXNpbmdfdW5pdHNfaGlnaF9zcGVlZF9pbnRlcm5ldCA9IEIyODAwMl8wMDRFLCAgIyBQZXJjZW50YWdlIG9mIEhvdXNpbmcgVW5pdHMgd2l0aCBIaWdoLVNwZWVkIEludGVybmV0DQogICAgICAgICBob3VzaW5nX3VuaXRzX25vX3ZlaGljbGUgPSBCMjUwNDRfMDAzRSwgICAgICAgICAgICMgUGVyY2VudGFnZSBvZiBIb3VzaW5nIFVuaXRzIHdpdGggTm8gVmVoaWNsZSBBdmFpbGFibGUNCiAgICAgICAgIHJhY2Vfd2hpdGUgPSBCMDIwMDFfMDAyRSwgICAgICAgICAgICAgICAgICAgICAgICAgIyBSYWNlIGFuZCBFdGhuaWNpdHk6IFdoaXRlIEFsb25lDQogICAgICAgICByYWNlX2JsYWNrID0gQjAyMDAxXzAwM0UsICAgICAgICAgICAgICAgICAgICAgICAgICMgUmFjZSBhbmQgRXRobmljaXR5OiBCbGFjayBvciBBZnJpY2FuIEFtZXJpY2FuIEFsb25lDQogICAgICAgICBoaXNwYW5pY19sYXRpbm8gPSBCMDMwMDFfMDAzRSAgICAgICAgICAgICAgICAgICAgICMgUmFjZSBhbmQgRXRobmljaXR5OiBIaXNwYW5pYyBvciBMYXRpbm8NCiAgICAgICAgICkNCg0KIyBUcmFuc2Zvcm0gdGhlIGRhdGEgdG8gRVNSSToxMDI3MjggcHJvamVjdGlvbg0KdHJhY3RzMjEgPC0gdHJhY3RzMjEgJT4lIHN0X3RyYW5zZm9ybShzdF9jcnMoZGF0YSkpDQoNCmBgYA0KDQojIyBPcGVuIERhdGEgcGhpbGx5IA0KDQpwcml2YXRlIHNjaG9vbHMgcHJveGltaXR5LCBwYXJrcyBhbmQgbGFuZG1hcmtzLCBmbG9vZHBsYWlucywgZGFpbHkgYXJyZXN0cywgbGl0dGVyIHNjb3JlLCBoZWF0IGluZGV4DQoNCnBoaWxseSByaXNpbmcgYm91bmRhcmllcz8gDQoNCmBgYHtyIEFkZGluZyBEYXRhLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSAnaGlkZSd9DQoNCiMgTmVhcmVzdCBTY2hvb2xzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgU2Nob29scw0KDQpQaGlsbHlTY2hvb2xzIDwtDQogICBzdF9yZWFkKCIuL2RhdGEvU2Nob29scy5nZW9qc29uIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQpQaGlsbHlQdnRTY2hvb2xzIDwtDQogICBzdF9yZWFkKCIuL2RhdGEvU2Nob29scy5nZW9qc29uIikgJT4lDQogIGZpbHRlcihUWVBFX1NQRUNJRklDID09ICJQUklWQVRFIikgJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3Qgc2Nob29sDQoNCm5lYXJlc3Rfc2Nob29sIDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5U2Nob29scykNCm5lYXJlc3RfcHZ0X3NjaG9vbCA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseVB2dFNjaG9vbHMpDQoNCiMjIENvbnZlcnRpbmcgc2Nob29scyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlTY2hvb2xzKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF9zY2hvb2wgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3Rfc2Nob29sXSkNCg0KIyMgQ29udmVydGluZyBwcml2YXRlIHNjaG9vbHMgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5UHZ0U2Nob29scykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3RfcHZ0X3NjaG9vbCA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9wdnRfc2Nob29sXSkNCg0KIyBOZWFyZXN0IENvbGxlZ2VzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgQ29sbGVnZXMNCg0KUGhpbGx5Q29sbGVnZXMgPC0NCiBzdF9yZWFkKCIuL2RhdGEvVW5pdmVyc2l0aWVzX0NvbGxlZ2VzLmdlb2pzb24iKSU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IGNvbGxlZ2UNCg0KbmVhcmVzdF9jb2xsZWdlIDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5Q29sbGVnZXMpDQoNCiMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5Q29sbGVnZXMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X2NvbGxlZ2UgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfY29sbGVnZV0pDQoNCiMgTmVhcmVzdCBMYW5kbWFya3MNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBsYW5kbWFya3MgDQoNClBoaWxseUxhbmRtYXJrcyA8LQ0KIHN0X3JlYWQoImh0dHBzOi8vc2VydmljZXMuYXJjZ2lzLmNvbS9mTGVHamI3dTR1WHFlRjlxL2FyY2dpcy9yZXN0L3NlcnZpY2VzL0xhbmRtYXJrX1BvaW50cy9GZWF0dXJlU2VydmVyLzAvcXVlcnk/b3V0RmllbGRzPSomd2hlcmU9MSUzRDEmZj1nZW9qc29uIiklPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIE1hcHBpbmcgbmVhcmVzdCBsYW5kbWFyaw0KDQpuZWFyZXN0X2xhbmRtYXJrIDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5TGFuZG1hcmtzKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseUxhbmRtYXJrcykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3RfbGFuZG1hcmsgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfbGFuZG1hcmtdKQ0KDQojIE5lYXJlc3QgQ29tbWVyY2lhbCBDb3JyaWRvcnMNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBDb21tZXJjaWFsIENvcnJpZG9ycyANCg0KUGhpbGx5Q29tQ29yciA8LQ0KICBzdF9yZWFkKCIuL2RhdGEvQ29tbWVyY2lhbF9Db3JyaWRvcnMuZ2VvanNvbiIpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkNCg0KIyMgSXMgaXQgd2l0aGluIHRoZSBjb21tZXJjaWFsIGNvcnJpZG9yPw0KDQpkYXRhJHdpdGhpbl9jb21fY29yciA8LSBpZmVsc2Uoc3Rfd2l0aGluKGRhdGEsIFBoaWxseUNvbUNvcnIpLCAxLCAwKQ0KDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZSh3aXRoaW5fY29tX2NvcnIgPSBpZmVsc2UoaXMubmEod2l0aGluX2NvbV9jb3JyKSwgMCwgMSkpDQoNCiMjIE1hcHBpbmcgbmVhcmVzdCBjb21tZXJjaWFsIGNvcnJpZG9yDQoNCm5lYXJlc3RfY29ycmlkb3IgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlDb21Db3JyKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseUNvbUNvcnIpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19jb21tX2NvcnIgPC0gcnNnZW86OmRpc3RhbmNlX2V1Y2xpZGVhbl9wYWlyd2lzZSh4LCB5W25lYXJlc3RfY29ycmlkb3JdKQ0KDQojIExpdHRlciBJbmRleA0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhJ3MgTGl0dGVyIEluZGV4DQoNClBoaWxseUxpdHRlciA8LQ0KICBzdF9yZWFkKCIuL2RhdGEvTGl0dGVyX0luZGV4Lmdlb2pzb24iKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIEpvaW5pbmcgdGhlIGxpdHRlciBzY29yZQ0KDQpkYXRhIDwtIA0KIHN0X2pvaW4oZGF0YSwoUGhpbGx5TGl0dGVyICU+JQ0KICAgICAgICAgIHNlbGVjdCgtT0JKRUNUSUQsIC1TaGFwZV9fQXJlYSwgLVNoYXBlX19MZW5ndGggKSU+JQ0KICAgICAgICAgIHJlbmFtZShsaXR0ZXIgPSBTQ09SRSkpKSANCg0KI05lYXJlc3QgRmxvb2QgUGxhaW5zDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEncyBGbG9vZCBQbGFpbg0KDQpQaGlsbHlGbG9vZCA8LSANCiAgc3RfcmVhZCgiLi9kYXRhL0ZFTUFfMTAwX2Zsb29kX1BsYWluLmdlb2pzb24iKSAlPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIElzIGl0IHdpdGhpbiB0aGUgZmxvb2RwbGFpbj8NCg0KZGF0YSR3aXRoaW5fZmxvb2QgPC0gaWZlbHNlKHN0X3dpdGhpbihkYXRhLCBQaGlsbHlGbG9vZCksIDEsIDApDQoNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKHdpdGhpbl9mbG9vZCA9IGlmZWxzZShpcy5uYSh3aXRoaW5fZmxvb2QpLCAwLCAxKSkNCg0KIyMgTWFwcGluZyBuZWFyZXN0IGZsb29kcGxhaW4gDQoNCm5lYXJlc3RfZmxvb2RwbGFpbiA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseUZsb29kKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseUZsb29kKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fZmxvb2RwbGFpbiA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9mbG9vZHBsYWluXSkNCg0KIyBOZWFyZXN0IFRyYW5zaXQgU3RvcHMNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSdzIFRyYW5zaXQgU3RvcHMNCg0KZWwgPC0gc3RfcmVhZCgiaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzhjNmUyNTc1YzhhZDQ2ZWI4ODdlNmJiMzU4MjVlMWE2XzAuZ2VvanNvbiIpDQpCcm9hZF9TdCA8LSBzdF9yZWFkKCJodHRwczovL29wZW5kYXRhLmFyY2dpcy5jb20vZGF0YXNldHMvMmU5MDM3ZmQ1YmVmNDA2NDg4ZmZlNWJiNjdkMjEzMTJfMC5nZW9qc29uIikNCg0KUGhpbGx5U2VwdGFTdG9wcyA8LSANCiAgcmJpbmQoDQogICAgIGVsICU+JSANCiAgICAgIG11dGF0ZShMaW5lID0gIkVsIikgJT4lDQogICAgICBkcGx5cjo6c2VsZWN0KFN0YXRpb24sIExpbmUpLA0KICAgICBCcm9hZF9TdCAlPiUNCiAgICAgIG11dGF0ZShMaW5lID0iQnJvYWRfU3QiKSAlPiUNCiAgICAgIGRwbHlyOjpzZWxlY3QoU3RhdGlvbiwgTGluZSkpICU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKHRyYWN0czIxKSkgIA0KDQojIyBNYXBwaW5nIG5lYXJlc3QgdHJhbnNpdCBzdG9wDQoNCm5lYXJlc3Rfc3RhdGlvbiA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseVNlcHRhU3RvcHMpDQoNCiMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5U2VwdGFTdG9wcykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3Rfc3RhdGlvbiA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9zdGF0aW9uXSkNCg0KIyBOZWFyZXN0IFRyYWlscw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIFRyYWlscyANCg0KUGhpbGx5VHJhaWxzIDwtDQogc3RfcmVhZCgiLi9kYXRhL1BQUl9UcmFpbHMuZ2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgdHJhaWwNCg0KbmVhcmVzdF90cmFpbCA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseVRyYWlscykNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlUcmFpbHMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X3RyYWlsIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X3RyYWlsXSkNCg0KIyBOZWFyZXN0IFRlbm5pcyBDb3VydHMNCiMjIEFkZGluZyBkYXRhIG9uIFBoaWxhZGVscGhpYSBUZW5uaXMgQ291cnRzIA0KDQpQaGlsbHlUZW5uaXNDb3VydHMgPC0NCiBzdF9yZWFkKCIuL2RhdGEvUFBSX1Rlbm5pc19Db3VydHMuZ2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgdGVubmlzIGNvdXJ0DQoNCm5lYXJlc3RfdGVubmlzY291cnQgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlUZW5uaXNDb3VydHMpDQoNCiMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5VGVubmlzQ291cnRzKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF90ZW5uaXNjb3VydCA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF90ZW5uaXNjb3VydF0pDQoNCiMgTmVhcmVzdCBQbGF5Z3JvdW5kcw0KIyMgQWRkaW5nIGRhdGEgb24gUGhpbGFkZWxwaGlhIFBsYXlncm91bmRzIA0KDQpQaGlsbHlQbGF5Z3JvdW5kcyA8LQ0KIHN0X3JlYWQoIi4vZGF0YS9QUFJfUGxheWdyb3VuZHMuZ2VvanNvbiIpJT4lDQogIHN0X3RyYW5zZm9ybShzdF9jcnModHJhY3RzMjEpKQ0KDQojIyBNYXBwaW5nIG5lYXJlc3QgcGxheWdyb3VuZA0KDQpuZWFyZXN0X3BsYXlncm91bmQgPC0gc2Y6OnN0X25lYXJlc3RfZmVhdHVyZShkYXRhLCBQaGlsbHlQbGF5Z3JvdW5kcykNCg0KIyMgQ29udmVydGluZyB0byByc2dlbyBnZW9tZXRyaWVzDQoNCnggPC0gcnNnZW86OmFzX3JzZ2VvKGRhdGEpDQp5IDwtIHJzZ2VvOjphc19yc2dlbyhQaGlsbHlQbGF5Z3JvdW5kcykNCg0KIyMgQ2FsY3VsYXRpbmcgZGlzdGFuY2UNCg0KZGF0YSRkaXN0X3RvX25lYXJlc3RfcGxheWdyb3VuZCA8LSByc2dlbzo6ZGlzdGFuY2VfZXVjbGlkZWFuX3BhaXJ3aXNlKHgsIHlbbmVhcmVzdF9wbGF5Z3JvdW5kXSkNCg0KIyBOZWFyZXN0IFBvb2xzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgUG9vbHMgDQoNClBoaWxseVBvb2xzIDwtDQogc3RfcmVhZCgiLi9kYXRhL1BQUl9Td2ltbWluZ19Qb29scy5nZW9qc29uIiklPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIE1hcHBpbmcgbmVhcmVzdCBwb29sDQoNCm5lYXJlc3RfcG9vbCA8LSBzZjo6c3RfbmVhcmVzdF9mZWF0dXJlKGRhdGEsIFBoaWxseVBvb2xzKQ0KDQojIyBDb252ZXJ0aW5nIHRvIHJzZ2VvIGdlb21ldHJpZXMNCg0KeCA8LSByc2dlbzo6YXNfcnNnZW8oZGF0YSkNCnkgPC0gcnNnZW86OmFzX3JzZ2VvKFBoaWxseVBvb2xzKQ0KDQojIyBDYWxjdWxhdGluZyBkaXN0YW5jZQ0KDQpkYXRhJGRpc3RfdG9fbmVhcmVzdF9wb29sIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X3Bvb2xdKQ0KDQojIE5lYXJlc3QgUG9saWNlIFN0YXRpb25zDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgUG9saWNlIFN0YXRpb25zDQoNClBoaWxseVBvbGljZVN0YXRpb25zIDwtDQogc3RfcmVhZCgiLi9kYXRhL1BvbGljZV9TdGF0aW9ucy5nZW9qc29uIiklPiUNCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyh0cmFjdHMyMSkpDQoNCiMjIE1hcHBpbmcgbmVhcmVzdCBwb2xpY2Ugc3RhdGlvbg0KDQpuZWFyZXN0X3BvbGljZV9zdGF0aW9uIDwtIHNmOjpzdF9uZWFyZXN0X2ZlYXR1cmUoZGF0YSwgUGhpbGx5UG9saWNlU3RhdGlvbnMpDQoNCiMjIENvbnZlcnRpbmcgdG8gcnNnZW8gZ2VvbWV0cmllcw0KDQp4IDwtIHJzZ2VvOjphc19yc2dlbyhkYXRhKQ0KeSA8LSByc2dlbzo6YXNfcnNnZW8oUGhpbGx5UG9saWNlU3RhdGlvbnMpDQoNCiMjIENhbGN1bGF0aW5nIGRpc3RhbmNlDQoNCmRhdGEkZGlzdF90b19uZWFyZXN0X3BvbGljZV9zdGF0aW9uIDwtIHJzZ2VvOjpkaXN0YW5jZV9ldWNsaWRlYW5fcGFpcndpc2UoeCwgeVtuZWFyZXN0X3BvbGljZV9zdGF0aW9uXSkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBQaGlsYWRlbHBoaWEgU2hvb3RpbmdzDQojIyBBZGRpbmcgZGF0YSBvbiBQaGlsYWRlbHBoaWEgU2hvb3RpbmdzDQoNClBoaWxseVNob290aW5ncyA8LQ0KIHN0X3JlYWQoIi4vZGF0YS9zaG9vdGluZ3MyMDIxLmdlb2pzb24iKSU+JQ0KICBkcGx5cjo6c2VsZWN0KCJkYXRlXyIsICJwb2ludF94IiwgInBvaW50X3kiLCAiZ2VvbWV0cnkiKSU+JQ0KICBzdF90cmFuc2Zvcm0oc3RfY3JzKGRhdGEpKSU+JQ0KICBuYS5vbWl0KCkgDQoNCiMjY2FsY3VsYXRpbmcgZGlzdGFuY2UgdG8gbmVhcmVzdCBuZWlnaGJvcnMNCg0KbGlicmFyeShzZikNCmxpYnJhcnkoc3BhdHN0YXQpDQpsaWJyYXJ5KGNsYXNzKQ0KDQpkYXRhX3BwcCA8LSBhcy5wcHAoZGF0YSkNClBoaWxseVNob290aW5nc19wcHAgPC0gYXMucHBwKFBoaWxseVNob290aW5ncykNCg0KZGF0YSRjcmltZV9ubjUgPC0gbm5jcm9zcyhkYXRhX3BwcCwgUGhpbGx5U2hvb3RpbmdzX3BwcCwgayA9IDUpDQoNCmBgYA0KDQpgYGB7ciBQcmljZS9TcWZ0LCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0KdHJhY3RzMjEgPC0gDQogIHRyYWN0czIxICU+JQ0KICBtdXRhdGUoUGN0V2hpdGUgPSAoKHJhY2Vfd2hpdGUvdG90YWxwb3ApKjEwMCksDQogICAgICAgICBQY3RCbGFjayA9ICgocmFjZV9ibGFjay90b3RhbHBvcCkqMTAwKSwNCiAgICAgICAgIFBjdEhpc3BhbmljID0gKChoaXNwYW5pY19sYXRpbm8vdG90YWxwb3ApKjEwMCksDQogICAgICAgICBQY3RCYWNoZWxvcnMgPSAoKGVkdV9iYWNoZWxvcnMvdG90YWxwb3ApKjEwMCksDQogICAgICAgICBQY3RQb3ZlcnR5ID0gKChwb3BfYmVsb3dfcG92ZXJ0eS90b3RhbHBvcCkqMTAwKSkNCg0KYGBgDQoNCmBgYHtyfQ0KDQojIGpvaW5pbmcgY2Vuc3VzIGRhdGENCg0KZGF0YSA8LSANCiAgc3Rfam9pbihkYXRhLCB0cmFjdHMyMSkNCg0KYGBgDQoNCmBgYHtyfQ0KDQojc2F2aW5nIGZvciBwcmVkaWN0aW9uIA0KDQpkYXRhX29nIDwtIGRhdGENCg0KZGF0YSA8LSBkYXRhICU+JQ0KICBmaWx0ZXIoc2FsZV9wcmljZSA+IDApIA0KDQpgYGANCg0KDQojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCg0KYGBge3J9DQpJbnRlcm5hbFZhcmlhYmxlcyA8LSBkYXRhIA0KDQpJbnRlcm5hbFZhcmlhYmxlcyA8LSBzdF9kcm9wX2dlb21ldHJ5KEludGVybmFsVmFyaWFibGVzKQ0KDQpJbnRlcm5hbFZhcmlhYmxlcyA8LSBJbnRlcm5hbFZhcmlhYmxlcyAlPiUNCiAgZHBseXI6OnNlbGVjdCgic2FsZV9wcmljZSIsICJQcmljZVBlclNxZnQiLCAidG90YWxfbGl2YWJsZV9hcmVhIiwgInllYXJfYnVpbHQiLCAibnVtYmVyX29mX3Jvb21zIiwgIm51bWJlcl9vZl9iYXRocm9vbXMiLCAibnVtYmVyX29mX2JlZHJvb21zIikgDQoNCnN0YXJnYXplcihhcy5kYXRhLmZyYW1lKEludGVybmFsVmFyaWFibGVzKSwgdHlwZT0idGV4dCIsIGRpZ2l0cz0xLCB0aXRsZSA9ICJEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzIGZvciBQaGlsYWRlbHBoaWEgSG9tZXMgSW50ZXJuYWwgVmFyaWFibGVzIChGaWd1cmUgNC4xKSIsIG91dCA9ICJUcmFpbmluZ19QSExJbnRlcm5hbC50eHQiKQ0KYGBgDQoNCmBgYHtyfQ0KRGVtb2dyYXBoaWNWYXJpYWJsZXMgPC0gZGF0YSANCg0KRGVtb2dyYXBoaWNWYXJpYWJsZXMgPC0gc3RfZHJvcF9nZW9tZXRyeShEZW1vZ3JhcGhpY1ZhcmlhYmxlcykNCg0KRGVtb2dyYXBoaWNWYXJpYWJsZXMgPC0gRGVtb2dyYXBoaWNWYXJpYWJsZXMgJT4lDQogIGRwbHlyOjpzZWxlY3QoIlBjdFdoaXRlIiwgIlBjdEJsYWNrIiwgIlBjdEhpc3BhbmljIiwgIlBjdEJhY2hlbG9ycyIsICJQY3RQb3ZlcnR5IiwgIm1lZF9pbmNvbWUiKSANCg0Kc3RhcmdhemVyKGFzLmRhdGEuZnJhbWUoRGVtb2dyYXBoaWNWYXJpYWJsZXMpLCB0eXBlPSJ0ZXh0IiwgZGlnaXRzPTEsIHRpdGxlID0gIkRlc2NyaXB0aXZlIFN0YXRpc3RpY3MgZm9yIFBoaWxhZGVscGhpYSBIb21lcyBEZW1vZ3JhcGhpYyBWYXJpYWJsZXMgKEZpZ3VyZSA0LjEpIiwgb3V0ID0gIlRyYWluaW5nX1BITFNwYXRpYWwudHh0IikNCmBgYA0KYGBge3J9DQoNClNwYXRpYWxWYXJpYWJsZXMgPC0gZGF0YSANCg0KU3BhdGlhbFZhcmlhYmxlcyA8LSBzdF9kcm9wX2dlb21ldHJ5KFNwYXRpYWxWYXJpYWJsZXMpDQoNClNwYXRpYWxWYXJpYWJsZXMgPC0gU3BhdGlhbFZhcmlhYmxlcyAlPiUNCiAgZHBseXI6OnNlbGVjdCgiZGlzdF90b19uZWFyZXN0X3NjaG9vbCIsICJkaXN0X3RvX25lYXJlc3RfcHZ0X3NjaG9vbCIsICJkaXN0X3RvX25lYXJlc3RfbGFuZG1hcmsiLCAiZGlzdF90b19jb21tX2NvcnIiLCAiZGlzdF90b19uZWFyZXN0X3N0YXRpb24iLCAiZGlzdF90b19uZWFyZXN0X3RyYWlsIiwgImRpc3RfdG9fbmVhcmVzdF9wb2xpY2Vfc3RhdGlvbiIsImNyaW1lX25uNSIpIA0KDQpzdGFyZ2F6ZXIoYXMuZGF0YS5mcmFtZShTcGF0aWFsVmFyaWFibGVzKSwgdHlwZT0idGV4dCIsIGRpZ2l0cz0xLCB0aXRsZSA9ICJEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzIGZvciBQaGlsYWRlbHBoaWEgSG9tZXMgU3BhdGlhbCBWYXJpYWJsZXMgKEZpZ3VyZSA0LjEpIiwgb3V0ID0gIlRyYWluaW5nX1BITFNwYXRpYWwudHh0IikNCmBgYA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTR9DQpDYXRlZ29yaWNhbFZhcmlhYmxlcyA8LSBkYXRhIA0KQ2F0ZWdvcmljYWxWYXJpYWJsZXMgPC0gc3RfZHJvcF9nZW9tZXRyeShDYXRlZ29yaWNhbFZhcmlhYmxlcykNCkNhdGVnb3JpY2FsVmFyaWFibGVzIDwtIENhdGVnb3JpY2FsVmFyaWFibGVzICU+JQ0KICBkcGx5cjo6c2VsZWN0KCJCYXNlbWVudFR5cGUiLCAiR2FyYWdlVHlwZSIsICJWaWV3VHlwZSIsICJFeHRlcmlvckNvbmRpdGlvblR5cGUiLCAiSW50ZXJpb3JDb25kaXRpb25UeXBlIiwgImJ1aWxkaW5nX2NvZGVfZGVzY3JpcHRpb25fbmV3Iiwic2FsZV9wcmljZSIpIA0KDQpDYXRlZ29yaWNhbFZhcmlhYmxlcyAlPiUNCiAgZ2F0aGVyKENhdGVnb3JpY2FsVmFyaWFibGVzLCBWYWx1ZSwgLXNhbGVfcHJpY2UpICU+JQ0KICBnZ3Bsb3QoYWVzKFZhbHVlLCBzYWxlX3ByaWNlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGZhY2V0X3dyYXAofkNhdGVnb3JpY2FsVmFyaWFibGVzLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2w9MikgKw0KICBsYWJzKHRpdGxlID0gIlByaWNlIGFzIGEgZnVuY3Rpb24gb2YgQ2F0ZWdvcmljYWwgVmFyaWFibGVzIikgKw0KICBwbG90VGhlbWUoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQoNCmBgYA0KDQoNCg0KIyMgTWFwcGluZyBJbnRlcm5hbCBWYXJpYWJsZXMNCg0KYGBge3J9DQoNCiMjTWFwcGluZyBJbnRlcm5hbCBWYXJpYWJsZXMNCiMgTWFwcGluZyBzYWxlIHByaWNlDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNShzYWxlX3ByaWNlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG4gU2FsZSBQcmljZSIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsNCiAgbGFicyh0aXRsZT0iUHJvcGVydGllcyBieSBTYWxlIFByaWNlIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMS4xIikgKw0KICBtYXBUaGVtZSgpDQoNCiMgTWFwcGluZyBTaXplDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNSh0b3RhbF9saXZhYmxlX2FyZWEpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjc1KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAidG90YWxfbGl2YWJsZV9hcmVhIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuQXJlYSBpbiBTcSBGdCIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsNCiAgbGFicyh0aXRsZT0iUHJvcGVydGllcyBieSBTaXplIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMS4yIikgKw0KICBtYXBUaGVtZSgpDQoNCiMgTWFwcGluZyBJbnRlcmlvciBDb25kaXRpb24NCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IChJbnRlcmlvckNvbmRpdGlvblR5cGUpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwgbmFtZT0iSW50ZXJpb3IgQ29uZGl0aW9uIiwNCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArDQogIGxhYnModGl0bGU9IlByb3BlcnRpZXMgYnkgSW50ZXJuYWwgQ29uZGl0aW9uIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMS4zIikgKw0KICBtYXBUaGVtZSgpDQoNCmBgYA0KDQoNCiMjIE1hcHBpbmcgU3BhdGlhbCBWYXJpYWJsZXMNCg0KYGBge3J9DQojIyBNYXBwaW5nIFNwYXRpYWwgVmFyaWFibGVzDQoNCiMgTWFwcGluZyBwcm9wZXJ0aWVzIGFyb3VuZCBzY2hvb2xzDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNShzYWxlX3ByaWNlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSwgYWxwaGE9MC4zKSArIA0KICBnZW9tX3NmKGRhdGEgPSBQaGlsbHlTY2hvb2xzLCBjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS41LCBhbHBoYT0wLjYpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGEsICJzYWxlX3ByaWNlIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuU2FsZSBQcmljZXMiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArIA0KICBsYWJzKHRpdGxlPSJQcm9wZXJ0aWVzIEFyb3VuZCBTY2hvb2xzIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMi4xIikgKw0KICBtYXBUaGVtZSgpDQoNCiMgTWFwcGluZyBwcm9wZXJ0aWVzIGFyb3VuZCBjb21tZXJjaWFsIGNvcnJpZG9ycw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUsIGFscGhhPTAuMykgKyANCiAgZ2VvbV9zZihkYXRhID0gUGhpbGx5Q29tQ29yciwgY29sb3VyID0gImJsYWNrIiwgZmlsbD0iYmxhY2siLCBhbHBoYT0wLjYpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGEsICJzYWxlX3ByaWNlIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuU2FsZSBQcmljZXMiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArIA0KICBsYWJzKHRpdGxlPSJQcm9wZXJ0aWVzIEFyb3VuZCBDb21tZXJjaWFsIENvcnJpZG9ycyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjIuMiIpICsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgcHJvcGVydGllcyBhcm91bmQgbGFuZG1hcmtzDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNShzYWxlX3ByaWNlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC43NSwgYWxwaGE9MC4zKSArIA0KICBnZW9tX3NmKGRhdGEgPSBQaGlsbHlMYW5kbWFya3MsIGNvbG91ciA9ICJibGFjayIsIGZpbGw9ImJsYWNrIiwgc2l6ZSA9IC43NSwgYWxwaGE9MC42KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iUHJvcGVydGllcyBBcm91bmQgTGFuZG1hcmtzIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMi4zIikgKw0KICBtYXBUaGVtZSgpDQoNCiMgTWFwcGluZyBwcm9wZXJ0aWVzIGFyb3VuZCBmbG9vZHBsYWlucw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUsIGFscGhhPTAuMykgKyANCiAgZ2VvbV9zZihkYXRhID0gUGhpbGx5Rmxvb2QsIGNvbG91ciA9ICJibGFjayIsIGZpbGw9ImJsYWNrIiwgc2l6ZSA9IC43NSwgYWxwaGE9MC42KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iUHJvcGVydGllcyBBcm91bmQgRmxvb2QgUGxhaW4iLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWEgMjAyMi0yMDIzIiwgDQogICAgICBjYXB0aW9uPSJGaWd1cmUgMy4yLjQiKSArDQogIG1hcFRoZW1lKCkNCg0KI2xpYnJhcnkoZ3JpZEV4dHJhKQ0KDQojZ3JpZC5hcnJhbmdlKA0KICAjYjEsDQogICNiMiwNCiAgI2IzLA0KICAjYjQsDQogICNucm93ID0gMiwNCiAgI3dpZHRocz1jKDQsNCksDQogICN0b3AgPSAiVGl0bGUgb2YgdGhlIHBhZ2UiDQogICMpDQoNCg0KYGBgDQoNCg0KIyMgTWFwcGluZyBEZW1vZ3JhcGhpYyBWYXJpYWJsZXMNCg0KDQoNCmBgYHtyfQ0KIyMgTWFwcGluZyBEZW1vZ3JhcGhpYyBWYXJpYWJsZXMNCg0KIyBNYXBwaW5nIG1lZGlhbiBpbmNvbWUgYXJvdW5kIHNvbGQgcHJvcGVydGllcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgYWVzKGZpbGwgPSBxNShtZWRfaW5jb21lKSksIGNvbG9yID0gInRyYW5zcGFyZW50IiwgYWxwaGE9MC41KSArDQogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJCdVB1IiwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBxQnIodHJhY3RzMjEsICJtZWRfaW5jb21lIiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJNZWRpYW4gSW5jb21lXG5RdWludGlsZSBCcmVha3MiKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdGEsIGFlcyhjb2xvdXIgPSBxNShzYWxlX3ByaWNlKSksIA0KICAgICAgICAgIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC4yKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcihkYXRhLCAic2FsZV9wcmljZSIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlIEJyZWFrczpcblNhbGUgUHJpY2VzIiwgDQogICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSBOQSkgKyANCiAgbGFicyh0aXRsZT0iTWVkaWFuIEluY29tZSBBcm91bmQgU29sZCBQcm9wZXJ0aWVzIiwgc3VidGl0bGUgPSAiUGhpbGFkZWxwaGlhIDIwMjItMjAyMyIsIA0KICAgICAgY2FwdGlvbj0iRmlndXJlIDMuMy4xIikrDQogIG1hcFRoZW1lKCkNCg0KDQoNCiMgTWFwcGluZyB3aGl0ZSBwb3B1bGF0aW9uIGFyb3VuZCBzb2xkIHByb3BlcnRpZXMNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGFlcyhmaWxsID0gcTUoUGN0V2hpdGUpKSwgY29sb3IgPSAidHJhbnNwYXJlbnQiLCBhbHBoYT0wLjUpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJ1UHUiLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHFCcih0cmFjdHMyMSwgIlBjdFdoaXRlIiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIlIFdoaXRlIFBvcHVsYXRpb25cblF1aW50aWxlIEJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IHE1KHNhbGVfcHJpY2UpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjIpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGEsICJzYWxlX3ByaWNlIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuU2FsZSBQcmljZXMiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArIA0KICBsYWJzKHRpdGxlPSIlIFdoaXRlIFBvcHVsYXRpb24gQXJvdW5kIFNvbGQgUHJvcGVydGllcyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjMuMiIpKw0KICBtYXBUaGVtZSgpDQoNCiMgTWFwcGluZyBibGFjayBwb3B1bGF0aW9uIGFyb3VuZCBzb2xkIHByb3BlcnRpZXMNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGFlcyhmaWxsID0gcTUoUGN0QmxhY2spKSwgY29sb3IgPSAidHJhbnNwYXJlbnQiLCBhbHBoYT0wLjUpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJ1UHUiLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHFCcih0cmFjdHMyMSwgIlBjdEJsYWNrIiksDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIlIEJsYWNrIFBvcHVsYXRpb25cblF1aW50aWxlIEJyZWFrcyIpICsNCiAgZ2VvbV9zZihkYXRhID0gZGF0YSwgYWVzKGNvbG91ciA9IHE1KHNhbGVfcHJpY2UpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjIpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKGRhdGEsICJzYWxlX3ByaWNlIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGUgQnJlYWtzOlxuU2FsZSBQcmljZXMiLCANCiAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9IE5BKSArIA0KICBsYWJzKHRpdGxlPSIlIEJsYWNrIFBvcHVsYXRpb24gQXJvdW5kIFNvbGQgUHJvcGVydGllcyIsIHN1YnRpdGxlID0gIlBoaWxhZGVscGhpYSAyMDIyLTIwMjMiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSAzLjMuMyIpKw0KICBtYXBUaGVtZSgpDQoNCiMgTWFwcGluZyBwb3B1bGF0aW9uIHdpdGggYmFjaGVsb3IncyBkZWdyZWUgYXJvdW5kIHNvbGQgcHJvcGVydGllcw0KDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImRhcmtncmV5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgYWVzKGZpbGwgPSBxNShQY3RCYWNoZWxvcnMpKSwgY29sb3IgPSAidHJhbnNwYXJlbnQiLCBhbHBoYT0wLjUpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJ1UHUiLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHFCcih0cmFjdHMyMSwgIlBjdEJhY2hlbG9ycyIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiJSBCYWNoZWxvcidzIERlZ3JlZVxuUXVpbnRpbGUgQnJlYWtzIikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuMikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IiUgUG9wdWxhdGlvbiB3aXRoIEJhY2hlbG9yJ3MgRGVncmVlIEFyb3VuZCBTb2xkIFByb3BlcnRpZXMiLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWEgMjAyMi0yMDIzIiwgDQogICAgICBjYXB0aW9uPSJGaWd1cmUgMy4zLjQiKSsNCiAgbWFwVGhlbWUoKQ0KDQojIE1hcHBpbmcgcG9wdWxhdGlvbiBpbiBwb3ZlcnR5IGFyb3VuZCBzb2xkIHByb3BlcnRpZXMNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSB0cmFjdHMyMSwgZmlsbCA9ICJncmV5ODkiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGFlcyhmaWxsID0gcTUoUGN0UG92ZXJ0eSkpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIsIGFscGhhPTAuNSkgKw0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQnVQdSIsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcUJyKHRyYWN0czIxLCAiUGN0UG92ZXJ0eSIpLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiJSBQb3AgaW4gUG92ZXJ0eVxuUXVpbnRpbGUgQnJlYWtzIikgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhLCBhZXMoY29sb3VyID0gcTUoc2FsZV9wcmljZSkpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuMikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVlLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1xQnIoZGF0YSwgInNhbGVfcHJpY2UiKSwNCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZSBCcmVha3M6XG5TYWxlIFByaWNlcyIsIA0KICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gTkEpICsgDQogIGxhYnModGl0bGU9IiUgUG9wdWxhdGlvbiBpbiBQb3ZlcnR5IEFyb3VuZCBTb2xkIFByb3BlcnRpZXMiLCBzdWJ0aXRsZSA9ICJQaGlsYWRlbHBoaWEgMjAyMi0yMDIzIiwgDQogICAgICBjYXB0aW9uPSJGaWd1cmUgMy4zLjUiKSsNCiAgbWFwVGhlbWUoKQ0KDQpgYGANCg0KDQoNCiMjIFNjYXR0ZXJwbG90cw0KDQoNCmBgYHtyIGZpZy5hbGlnbj0iY2VudGVyIiwgd2FybmluZz1GQUxTRSwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSAxNH0NCm9wdGlvbnMoc2NpcGVuID0gOTk5OSkgI3R1cm5zIG9mZiBzY2llbnRpZmljIG5vdGF0aW9uIGFuZCBob3cgbWFueSBwb2ludHMNCg0KVmFyaWFibGUgPC0gZGF0YSAlPiUNCiAgZmlsdGVyKHNhbGVfcHJpY2UgPCAxMDAwMDAwMCkNCg0KIyMgU2NhdHRlcnBsb3QNCnN0X2Ryb3BfZ2VvbWV0cnkoVmFyaWFibGUpICU+JSANCiAgZHBseXI6OnNlbGVjdChzYWxlX3ByaWNlLCBQcmljZVBlclNxZnQsIHllYXJfYnVpbHQsIG51bWJlcl9vZl9iYXRocm9vbXMsIG51bWJlcl9vZl9iZWRyb29tcywgbnVtYmVyX29mX3Jvb21zLCB0b3RhbF9saXZhYmxlX2FyZWEsIG1lZF9pbmNvbWUsIFBjdFdoaXRlLFBjdEJsYWNrLCBQY3RIaXNwYW5pYywgUGN0QmFjaGVsb3JzLCBQY3RQb3ZlcnR5LCApICU+JSANCiAgZmlsdGVyKHNhbGVfcHJpY2UgPCAxMDAwMDAwMCkgJT4lDQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1zYWxlX3ByaWNlKSAlPiUgDQogICBnZ3Bsb3QoYWVzKFZhbHVlLCBzYWxlX3ByaWNlKSkgKw0KICAgICBnZW9tX3BvaW50KHNpemUgPSAuNSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GLCBjb2xvdXIgPSAiI0ZBNzgwMCIpICsNCiAgICAgZmFjZXRfd3JhcCh+VmFyaWFibGUsIG5jb2wgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgICAgbGFicyh0aXRsZSA9ICJQcmljZSBhcyBhIGZ1bmN0aW9uIG9mIGNvbnRpbnVvdXMgdmFyaWFibGVzIiwgDQogICAgICAgICAgY2FwdGlvbj0iRmlndXJlIDQuMSIpICsNCiAgICAgcGxvdFRoZW1lKCkNCmBgYA0KDQoNCg0KDQojIyB0ZXN0aW5nIGNvcnJlbGF0aW9ucw0KDQpgYGB7cn0NCmludGVybmFsdmFyaWFibGVzIDwtIGMoDQogICJzYWxlX3ByaWNlIiwgIlByaWNlUGVyU3FmdCIsICJ0b3RhbF9saXZhYmxlX2FyZWEiLCAibnVtYmVyX29mX3Jvb21zIiwgIm51bWJlcl9vZl9iYXRocm9vbXMiLCAibnVtYmVyX29mX2JlZHJvb21zIiwgIkJ1aWxkaW5nQWdlIiwgImZyb250YWdlIiwgImRlcHRoIiwgImZpcmVwbGFjZXMiLCAiQmFzZW1lbnRQcmVzZW50IiwgIkdhcmFnZVByZXNlbnQiLCAiZXh0ZXJpb3JfY29uZGl0aW9uIiwgIm51bWJlcl9zdG9yaWVzIiwgImNlbnRyYWxfYWlyIg0KKQ0KDQpudW1lcmljaW50VmFycyA8LSBkYXRhICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KGRhdGEpICU+JQ0KICBzZWxlY3QoaW50ZXJuYWx2YXJpYWJsZXMpJT4lDQogIG5hLm9taXQoKQ0KDQpnZ2NvcnJwbG90KA0KICByb3VuZChjb3IobnVtZXJpY2ludFZhcnMpLCAxKSwgDQogIHAubWF0ID0gY29yX3BtYXQobnVtZXJpY2ludFZhcnMpLA0KICBjb2xvcnMgPSBjKCcjZDcxOTFjJywnI2ZmZmZiZicsJyMyYzdiYjYnKSwNCiAgdHlwZT0ibG93ZXIiLA0KICBpbnNpZyA9ICJibGFuayIpICsgIA0KICAgIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb24gYWNyb3NzIGludGVybmFsIHZhcmlhYmxlcyIpIA0KDQpgYGANCg0KYGBge3J9DQpkZW12YXJpYWJsZXMgPC0gYygNCiAgInNhbGVfcHJpY2UiLCAiUGN0V2hpdGUiLCAiUGN0QmxhY2siLCAiUGN0SGlzcGFuaWMiLCAiUGN0QmFjaGVsb3JzIiwgIlBjdFBvdmVydHkiLCAibWVkX2luY29tZSIsDQogIA0KICAibWVkX3JlbnQiLCAib3duZXJfdW5pdHMiLCAgICAgICAgICJyZW50ZXJfdW5pdHMiIA0KICAgICAgICAsICJzYW1lX2hvdXNlXzc1IiANCiAgICAgICAsICAic2FtZV9ob3VzZV8xIiANCiAgICAgICwgICAibW9udGhseV9jb3N0c19ub19tb3J0Z2FnZSIgDQogICAgICAsICAgIm1vbnRobHlfY29zdHNfd2l0aF9tb3J0Z2FnZSIgDQogICAgICAgICwgIm1lZF9ncm9zc19yZW50IiAgICAgDQogICAgICAgICwgImhvdXNpbmdfdW5pdHNfd2l0aF9oZWF0IiANCiAgICAgICAgLCAiZWR1X2JhY2hlbG9ycyIgDQogICAgICAgLCAgInBvcF9iZWxvd19wb3ZlcnR5IiANCiAgICAgICAsICAiaG91c2luZ191bml0c19oaWdoX3NwZWVkX2ludGVybmV0IiANCiAgICAgICAsICAiaG91c2luZ191bml0c19ub192ZWhpY2xlIiAgICAgICANCiAgKQ0KDQpudW1lcmljZGVtVmFycyA8LSBkYXRhICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KGRhdGEpICU+JQ0KICBzZWxlY3QoZGVtdmFyaWFibGVzKSU+JQ0KICBuYS5vbWl0KCkNCg0KZ2djb3JycGxvdCgNCiAgcm91bmQoY29yKG51bWVyaWNkZW1WYXJzKSwgMSksIA0KICBwLm1hdCA9IGNvcl9wbWF0KG51bWVyaWNkZW1WYXJzKSwNCiAgY29sb3JzID0gYygnI2Q3MTkxYycsJyNmZmZmYmYnLCcjMmM3YmI2JyksDQogIHR5cGU9Imxvd2VyIiwNCiAgaW5zaWcgPSAiYmxhbmsiKSArICANCiAgICBsYWJzKHRpdGxlID0gIkNvcnJlbGF0aW9uIGFjcm9zcyBEZW1vZ3JhcGhpYyB2YXJpYWJsZXMiKSANCg0KYGBgDQpgYGB7cn0NCnNwYXR2YXJpYWJsZXMgPC0gYygic2FsZV9wcmljZSIsICJkaXN0X3RvX25lYXJlc3Rfc2Nob29sIiwgImRpc3RfdG9fbmVhcmVzdF9wdnRfc2Nob29sIiwgImRpc3RfdG9fbmVhcmVzdF9sYW5kbWFyayIsICJkaXN0X3RvX2NvbW1fY29yciIsICJkaXN0X3RvX25lYXJlc3Rfc3RhdGlvbiIsICJkaXN0X3RvX25lYXJlc3RfdHJhaWwiLCAiZGlzdF90b19uZWFyZXN0X3BvbGljZV9zdGF0aW9uIiwiY3JpbWVfbm41IiwgImxpdHRlciIsICJ3aXRoaW5fY29tX2NvcnIiLCAid2l0aGluX2Zsb29kIiwgImRpc3RfdG9fZmxvb2RwbGFpbiIsICJkaXN0X3RvX25lYXJlc3RfdGVubmlzY291cnQiLCAiZGlzdF90b19uZWFyZXN0X3BsYXlncm91bmQiLCAiZGlzdF90b19uZWFyZXN0X3Bvb2wiLCAidG90YWxfbGl2YWJsZV9hcmVhIiAgICAgDQogICkNCg0KbnVtZXJpY3NwYXRWYXJzIDwtIGRhdGEgJT4lDQogIHN0X2Ryb3BfZ2VvbWV0cnkoZGF0YSkgJT4lDQogIHNlbGVjdChhbGxfb2Yoc3BhdHZhcmlhYmxlcykpJT4lDQogIG5hLm9taXQoKQ0KDQpnZ2NvcnJwbG90KA0KICByb3VuZChjb3IobnVtZXJpY3NwYXRWYXJzKSwgMSksIA0KICBwLm1hdCA9IGNvcl9wbWF0KG51bWVyaWNzcGF0VmFycyksDQogIGNvbG9ycyA9IGMoJyNkNzE5MWMnLCcjZmZmZmJmJywnIzJjN2JiNicpLA0KICB0eXBlPSJsb3dlciIsDQogIGluc2lnID0gImJsYW5rIikgKyAgDQogICAgbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbiBhY3Jvc3MgU3BhdGlhbCB2YXJpYWJsZXMiKSANCg0KYGBgDQoNCg0KanVzdGlmeSBjaG9pY2Ugb2YgdmFyaWJhbGVzIGhlcmUgDQoNCg0KYGBge3J9DQoNCiN2YXJpYWJsZXMgPC0gYygic2FsZV9wcmljZSIsICJudW1iZXJfb2ZfYmF0aHJvb21zIiwiZGlzdF90b19uZWFyZXN0X3B2dF9zY2hvb2wiLCAiZGlzdF90b19jb21tX2NvcnIiLCAiZGlzdF90b19uZWFyZXN0X3N0YXRpb24iLCAiZGlzdF90b19uZWFyZXN0X3RyYWlsIiwgIm1lZF9pbmNvbWUiLCAiZGlzdF90b19uZWFyZXN0X3Bvb2wiLCAiZnJvbnRhZ2UiLCAiY2VudHJhbF9haXIiLCAiZmlyZXBsYWNlcyIsICJCYXNlbWVudFByZXNlbnQiLCAiZXh0ZXJpb3JfY29uZGl0aW9uIiwgIlBjdFdoaXRlIiwgIlBjdEJsYWNrIiwgInRvdGFsX2xpdmFibGVfYXJlYSIsICJCdWlsZGluZ0FnZSIsICAgIlBjdEJhY2hlbG9ycyIsICJQY3RQb3ZlcnR5IiwgIlByaWNlUGVyU3FmdCIsInRvdGFsX2xpdmFibGVfYXJlYSIsICJob3VzaW5nX3VuaXRzX3dpdGhfaGVhdCIsICJQY3RIaXNwYW5pYyIsICJob3VzaW5nX3VuaXRzX2hpZ2hfc3BlZWRfbnRlcm5ldCIpDQoNCnZhcmlhYmxlcyA8LSBjICgic2FsZV9wcmljZSIgLCJ0b3RhbF9saXZhYmxlX2FyZWEiLCJudW1iZXJfb2ZfYmF0aHJvb21zIiwgImZpcmVwbGFjZXMiLCAiZXh0ZXJpb3JfY29uZGl0aW9uIiwgImNlbnRyYWxfYWlyIiwgIlBjdFdoaXRlIiwgIm1lZF9pbmNvbWUiLCAibW9udGhseV9jb3N0c193aXRoX21vcnRnYWdlIiAsICJQY3RQb3ZlcnR5IiwgImRpc3RfdG9fZmxvb2RwbGFpbiIsICJ3aXRoaW5fY29tX2NvcnIiKQ0KDQojZHJvcHBpbmcgY3JpbWUgZm9yIG5vdyBjaGVjayBsYXRlciANCg0KbnVtZXJpY1ZhcnMgPC0gZGF0YSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeShkYXRhKSAlPiUNCiAgc2VsZWN0KHZhcmlhYmxlcyklPiUNCiAgbmEub21pdCgpDQoNCmdnY29ycnBsb3QoDQogIHJvdW5kKGNvcihudW1lcmljVmFycyksIDEpLCANCiAgcC5tYXQgPSBjb3JfcG1hdChudW1lcmljVmFycyksDQogIGNvbG9ycyA9IGMoJyNkNzE5MWMnLCcjZmZmZmJmJywnIzJjN2JiNicpLA0KICB0eXBlPSJsb3dlciIsDQogIGluc2lnID0gImJsYW5rIikgKyAgDQogICAgbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbiBhY3Jvc3MgbnVtZXJpYyB2YXJpYWJsZXMiKSANCmBgYA0KDQojIFJlZ3Jlc3Npb25zDQoNCg0KDQpgYGB7cn0NCg0KIyMgQ3JlYXRpbmcgdGhlIHRyYWluaW5nIGFuZCB0ZXN0IHNldA0KbW9kZWxsaW5nX2RhdGEgPC0gZGF0YSAlPiUgZmlsdGVyKHRvUHJlZGljdCA9PSAiTU9ERUxMSU5HIikNCg0Kc2V0LnNlZWQoOTk5KSAjbWFrZXMgc3VyZSBkYXRhIGlzIHNwbGl0IHNhbWUgd2F5IGV2ZXJ5IHRpbWUNCg0KIyMgU3BsaXR0aW5nIHRoZSBkYXRhDQpzcGxpdCA8LSBzYW1wbGUuc3BsaXQobW9kZWxsaW5nX2RhdGEkb2JqZWN0aWQsIFNwbGl0UmF0aW8gPSAwLjc1KQ0KDQojIyBDcmVhdGluZyB0aGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cw0KdHJhaW5fZGF0YSA8LSBtb2RlbGxpbmdfZGF0YVtzcGxpdCxdDQp0ZXN0X2RhdGEgPC0gbW9kZWxsaW5nX2RhdGFbIXNwbGl0LF0NCmBgYA0KDQpgYGB7cn0NCg0KI3JlZ3Jlc3Npb24gb24gdHJhaW5pbmcgZGF0YQ0KDQpyZWcyIDwtIGxtKHNhbGVfcHJpY2UgfiAuLCBkYXRhID0gc3RfZHJvcF9nZW9tZXRyeSh0cmFpbl9kYXRhKSAlPiUgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCh2YXJpYWJsZXMpKQ0KDQojIyBQbG90IHJlZ3Jlc3Npb24gDQplZmZlY3RfcGxvdChyZWcyLCBwcmVkID0gbnVtYmVyX29mX2JhdGhyb29tcywgaW50ZXJ2YWwgPSBUUlVFLCBwbG90LnBvaW50cyA9IFRSVUUpDQpwbG90X3N1bW1zKHJlZzIsIHNjYWxlID0gVFJVRSkNCg0KDQp0ZXN0X2RhdGEgPC0NCiAgdGVzdF9kYXRhICU+JQ0KICBtdXRhdGUoUHJpY2UuUHJlZGljdCA9IHByZWRpY3QocmVnMiwgdGVzdF9kYXRhKSwNCiAgICAgICAgIFByaWNlLkVycm9yID0gUHJpY2UuUHJlZGljdCAtIHNhbGVfcHJpY2UsDQogICAgICAgICBQcmljZS5BYnNFcnJvciA9IGFicyhQcmljZS5QcmVkaWN0IC0gc2FsZV9wcmljZSksDQogICAgICAgICBQcmljZS5BUEUgPSAoYWJzKFByaWNlLlByZWRpY3QgLSBzYWxlX3ByaWNlKSkgLyBQcmljZS5QcmVkaWN0KQ0KDQpgYGANCg0KYGBge3J9DQojIHRhYmxlIG9mIE1BRSBhbmQgTUFQRQ0KDQojIyBBY2N1cmFjeSAtIE1lYW4gQWJzb2x1dGUgRXJyb3INCk1BRSA8LSBkYXRhLmZyYW1lKG1lYW4odGVzdF9kYXRhJFByaWNlLkFic0Vycm9yLCBuYS5ybSA9IFQpKQ0KTUFQRSA8LSBkYXRhLmZyYW1lKG1lYW4odGVzdF9kYXRhJFByaWNlLkFQRSwgbmEucm0gPSBUKSkgI01BUEUNCg0KDQpyZWcuTUFFLk1BUEUgPC0gY2JpbmQoTUFFLCBNQVBFKSAlPiUNCiAga2FibGUoZm9ybWF0ID0gImh0bWwiLCBjYXB0aW9uID0gIlJlZ3Jlc3Npb24gTUFFICYgTUFQRSAoRmlndXJlIDUuMSkiLCBjb2wubmFtZXMgPSBjKCJNZWFuIEFic29sdXRlIEVycm9yIiwgIk1lYW4gQWJzb2x1dGUgUGVyY2VudGFnZSBFcnJvciIpKSAlPiUNCiAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGQUxTRSkNCg0KcmVnLk1BRS5NQVBFDQoNCmBgYA0KDQpgYGB7cn0NCg0KdGVzdF9kYXRhIDwtDQogIHRlc3RfZGF0YSAlPiUNCiAgZmlsdGVyKHNhbGVfcHJpY2UgPCAxMDAwMDAwMCkNCg0KIyMgSy1Gb2xkOiBHZW5lcmFsaXphYmlsaXR5IENyb3NzLVZhbGlkYXRpb24NCg0KZml0Q29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTAwKQ0Kc2V0LnNlZWQoOTk5KQ0KDQpyZWcuY3YgPC0gDQogIHRyYWluKHNhbGVfcHJpY2UgfiAuLCBkYXRhID0gc3RfZHJvcF9nZW9tZXRyeSh0ZXN0X2RhdGEpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCh2YXJpYWJsZXMpLA0KbWV0aG9kID0gImxtIiwgdHJDb250cm9sID0gZml0Q29udHJvbCwgbmEuYWN0aW9uID0gbmEucGFzcykNCg0KI1RoaXMgbmVlZHMgdG8gYmUgd29yay1zaG9wcGVkLiBJdCBzdG9wcGVkIHdvcmtpbmcgYW5kIEkgZG9uJ3Qga25vdyB3aHkuIFRoZSBjb2RlIHdpdGhvdXQgYW4gb2JqZWN0IGFzc2lnbmVkIGFsc28gb3V0cHV0cy4NCg0KI3JlZy5jdiRyZXNhbXBsZVsxOjUsXQ0KDQojbWVhbihyZWcuY3YkcmVzYW1wbGVbLDNdKQ0KDQojcmVnLmN2JHJlc2FtcGxlWzcsXQ0KDQoNCnN0YXJnYXplcihhcy5kYXRhLmZyYW1lKHJlZy5jdiRyZXNhbXBsZSksIHR5cGU9InRleHQiLCBkaWdpdHM9MSwgdGl0bGU9IkNyb3NzIFZhbGlkYXRpb24gUmVzdWx0cyAoNS4yKSIsIG91dCA9ICJDVi50eHQiKSAjYWxsIGN2DQoNCmBgYA0KDQpgYGB7cn0NCiNwbG90dGluZyB0aGUgY3Jvc3MgdmFsaWRhdGlvbiBzdHVmZg0KDQpnZ3Bsb3QocmVnLmN2JHJlc2FtcGxlLCBhZXMoeD1NQUUpKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiIzJjN2JiNiIpICsNCiAgbGFicyh0aXRsZSA9ICJDcm9zcyBWYWxpZGF0aW9uIFRlc3RzIGluIE1lYW4gQXZlcmFnZSBFcnJvciIsIGNhcHRpb249IkZpZ3VyZSA1LjMiKSArDQogIHBsb3RUaGVtZSgpDQoNCmBgYA0KDQpgYGB7cn0NCg0KdGVzdF9kYXRhICU+JQ0KICBkcGx5cjo6c2VsZWN0KFByaWNlLlByZWRpY3QsIHNhbGVfcHJpY2UpICU+JQ0KICAgIGdncGxvdChhZXMoc2FsZV9wcmljZSwgUHJpY2UuUHJlZGljdCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc3RhdF9zbW9vdGgoYWVzKHNhbGVfcHJpY2UsIHNhbGVfcHJpY2UpLCANCiAgICAgICAgICAgICBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBzaXplID0gMSwgY29sb3VyPSIjZDcxOTFjIikgKyANCiAgc3RhdF9zbW9vdGgoYWVzKFByaWNlLlByZWRpY3QsIHNhbGVfcHJpY2UpLCANCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgc2l6ZSA9IDEsIGNvbG91cj0iIzJjN2JiNiIpICsNCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIFNhbGUgUHJpY2UgYXMgYSBGdW5jdGlvbiBvZiBPYnNlcnZlZCBQcmljZSIsDQogICAgICAgc3VidGl0bGU9IlJlZCBsaW5lIHJlcHJlc2VudHMgYSBwZXJmZWN0IHByZWRpY3Rpb247IEJsdWUgbGluZSByZXByZXNlbnRzIHByZWRpY3Rpb24iLA0KICAgICAgIGNhcHRpb24gPSAiRmlndXJlIDYuMiIsDQogICAgICAgeCA9ICJPYnNlcnZlZCBQcmljZSIsDQogICAgICAgeSA9ICJQcmVkaWN0ZWQgUHJpY2UiKSArDQogIHBsb3RUaGVtZSgpDQpgYGANCg0KYGBge3J9DQoNCiN1c2UgdGhlIHNhbWUgdGhuZyBmb3Igd2lnaHRzIGFuZCBtb3JhbnMgaSANCiNtb3ZlIHRoZSBmaWx0ZXJzIHRvIHRoaXMgY2VsIHRvbyANCg0KDQojIFNwYXRpYWwgTGFnIGZvciBwcmljZQ0KY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKHRlc3RfZGF0YSkgDQpuZWlnaGJvckxpc3QgPC0ga25uMm5iKGtuZWFybmVpZ2goY29vcmRzLCA1KSkNCnNwYXRpYWxXZWlnaHRzIDwtIG5iMmxpc3R3KG5laWdoYm9yTGlzdCwgc3R5bGU9IlciKQ0KdGVzdF9kYXRhJGxhZ1ByaWNlIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhJHNhbGVfcHJpY2UpDQoNCnRlc3RfZGF0YSRsYWdQcmljZUVycm9yIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhJFByaWNlLkVycm9yLCBOQU9LID0gVFJVRSkNCg0KIyBGaWx0ZXJpbmcgZ3JlYXRlciB0aGFuIDAgdmFsdWVzDQoNCnRlc3RfZGF0YV9maWx0ZXIgPC0gdGVzdF9kYXRhICU+JQ0KICBmaWx0ZXIoUHJpY2UuRXJyb3IgPiAwLCBsYWdQcmljZUVycm9yID4gMCkNCg0KZ2dwbG90KGRhdGEgPSB0ZXN0X2RhdGFfZmlsdGVyLCBhZXMobGFnUHJpY2VFcnJvciwgUHJpY2UuRXJyb3IpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IC44NSxjb2xvdXIgPSAiYmxhY2siKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLGNvbG91ciA9ICJyZWQiLHNpemUgPSAxLjIpICsNCiAgbGFicyh0aXRsZT0iUHJpY2UgRXJyb3JzIiwNCiAgICAgICBjYXB0aW9uID0gIkZpZ3VyZSA2LjEiKSArDQogIHBsb3RUaGVtZSgpDQoNCmBgYA0KDQpgYGB7cn0NCg0KIyBmb3IgbW9yYW5zIDENCg0KIyBTcGF0aWFsIExhZyBmb3IgcHJpY2UNCmNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyh0ZXN0X2RhdGFfZmlsdGVyKSANCm5laWdoYm9yTGlzdCA8LSBrbm4ybmIoa25lYXJuZWlnaChjb29yZHMsIDUpKQ0Kc3BhdGlhbFdlaWdodHMgPC0gbmIybGlzdHcobmVpZ2hib3JMaXN0LCBzdHlsZT0iVyIpDQp0ZXN0X2RhdGFfZmlsdGVyJGxhZ1ByaWNlIDwtIGxhZy5saXN0dyhzcGF0aWFsV2VpZ2h0cywgdGVzdF9kYXRhX2ZpbHRlciRzYWxlX3ByaWNlKQ0KDQp0ZXN0X2RhdGFfZmlsdGVyJGxhZ1ByaWNlRXJyb3IgPC0gbGFnLmxpc3R3KHNwYXRpYWxXZWlnaHRzLCB0ZXN0X2RhdGFfZmlsdGVyJFByaWNlLkVycm9yLCBOQU9LID0gVFJVRSkNCg0KDQoNCm1vcmFuVGVzdCA8LSBtb3Jhbi5tYyhuYS5vbWl0KHRlc3RfZGF0YV9maWx0ZXIkUHJpY2UuRXJyb3IpLA0KICAgICAgICAgICAgICAgICAgICAgIHNwYXRpYWxXZWlnaHRzLCBuc2ltID0gOTk5KSAgDQoNCmdncGxvdChhcy5kYXRhLmZyYW1lKG1vcmFuVGVzdCRyZXNbYygxOjk5OSldKSwgYWVzKG1vcmFuVGVzdCRyZXNbYygxOjk5OSldKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDA1KSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtb3JhblRlc3Qkc3RhdGlzdGljKSwgY29sb3VyID0gIiNkNzE5MWMiLHNpemU9MSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtMC41LDAuNSkpICsNCiAgbGFicyh0aXRsZT0iT2JzZXJ2ZWQgYW5kIHBlcm11dGVkIE1vcmFuJ3MgSSIsDQogICAgICAgc3VidGl0bGU9ICJPYnNlcnZlZCBNb3JhbidzIEkgaW4gcmVkIiwNCiAgICAgICBjYXB0aW9uID0gIkZpZ3VyZSA2LjMiLA0KICAgICAgIHg9Ik1vcmFuJ3MgSSIsDQogICAgICAgeT0iQ291bnQiKSArDQogIHBsb3RUaGVtZSgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHRyYWN0czIxLCBmaWxsID0gImdyZXk4OSIsIGNvbG9yID0gImdyZXk4OSIpICsNCiAgZ2VvbV9zZihkYXRhID0gdGVzdF9kYXRhLCBhZXMoY29sb3VyID0gcTUoUHJpY2UuQWJzRXJyb3IpKSwgDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSAicG9pbnQiLCBzaXplID0gLjc1KSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZWUsDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPXFCcih0ZXN0X2RhdGEsIlByaWNlLkFic0Vycm9yIiksDQogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsNCiAgbGFicyh0aXRsZT0iTWFwIG9mIFByaWNlIEFic29sdXRlIEVycm9ycywgUGhpbGwiLCANCiAgICAgIGNhcHRpb249IkZpZ3VyZSA2LjQiKSArDQogIG1hcFRoZW1lKCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gdHJhY3RzMjEsIGZpbGwgPSAiZ3JleTg5IiwgY29sb3IgPSAiZ3JleTg5IikgKw0KICBnZW9tX3NmKGRhdGEgPSB0ZXN0X2RhdGEsIGFlcyhjb2xvdXIgPSBxNShQcmljZS5BYnNFcnJvcikpLCANCiAgICAgICAgICBzaG93LmxlZ2VuZCA9ICJwb2ludCIsIHNpemUgPSAuNzUpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlZSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9cUJyKHRlc3RfZGF0YSwiUHJpY2UuUHJlZGljdCIpLA0KICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArDQogIGxhYnModGl0bGU9InByZWRpY3RlZCBzYWxlIHByaWNlIiwgDQogICAgICBjYXB0aW9uPSJGaWd1cmUgNi40IikgKw0KICBtYXBUaGVtZSgpDQpgYGANCg0KYGBge3J9DQoNCiN0aGlzIGlzIHdyb25nIA0KDQpzdF9kcm9wX2dlb21ldHJ5KHRlc3RfZGF0YSkgJT4lDQogIGdyb3VwX2J5KGNlbnN1c190cmFjdCkgJT4lDQogIHN1bW1hcml6ZShNQVBFID0gbWVhbihQcmljZS5BUEUsIG5hLnJtID0gVCkpICU+JQ0KICB1bmdyb3VwKCkgJT4lIA0KICBjcm9zc19qb2luKCB0cmFjdHMyMSklPiUNCiAgICBzdF9zZigpICU+JQ0KICAgIGdncGxvdCgpICsgDQogICBnZW9tX3NmKGFlcyhmaWxsID0gTUFQRSkpICsNCiAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gcGFsZXR0ZWVbNV0sIGhpZ2ggPSBwYWxldHRlZVsxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJNQVBFIikgKw0KICBnZW9tX3NmKGRhdGEgPSB0ZXN0X2RhdGEsIGNvbG91ciA9ICJibGFjayIsIHNob3cubGVnZW5kID0gInBvaW50Iiwgc2l6ZSA9IC41KSArDQogICAgICBsYWJzKHRpdGxlID0gIk1lYW4gdGVzdCBzZXQgTUFQRSBieSBCbG9jayBHcm91cHMiLA0KICAgICAgICAgICBjYXB0aW9uID0gIkZpZ3VyZSA3LjMiKSArDQogICAgICBtYXBUaGVtZSgpDQoNCmBgYA0KDQpgYGB7cn0NCg0KdGVzdF9kYXRhIDwtDQogIHRlc3RfZGF0YSAlPiUNCiAgZ3JvdXBfYnkoY2Vuc3VzX3RyYWN0KSAlPiUNCiAgbXV0YXRlKE1lYW5QcmljZSA9IG1lYW4oc2FsZV9wcmljZSkpDQoNCnRlc3RfZGF0YSA8LQ0KICB0ZXN0X2RhdGEgJT4lDQogIGdyb3VwX2J5KGNlbnN1c190cmFjdCkgJT4lDQogIG11dGF0ZShNQVBFID0gbWVhbihQcmljZS5BUEUpKSAlPiUNCiAgZmlsdGVyKE1BUEU+IC0xMDApDQoNCmdncGxvdCh0ZXN0X2RhdGEpICsNCiAgZ2VvbV9wb2ludChhZXMoTWVhblByaWNlLCBNQVBFKSkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBhZXMoTWVhblByaWNlLCBNQVBFKSwgc2UgPSBGQUxTRSwgY29sb3VyID0gImdyZWVuIikgKw0KICBsYWJzKHRpdGxlID0gIk1BUEUgYnkgQmxvY2sgR3JvdXAgYXMgYSBmdW5jdGlvbiBvZiBtZWFuIHByaWNlIGJ5IEJsb2NrIEdyb3VwIiwNCiAgICAgICB4ID0gIk1lYW4gSG9tZSBQcmljZSIsIHkgPSAiTUFQRSIsDQogICAgICAgY2FwdGlvbiA9ICJGaWd1cmUgNy41IikgKw0KICBwbG90VGhlbWUoKQ0KYGBgDQoNCiMgUHJlZGljdGlvbiBDaGFsbGVuZ2UgDQoNCmBgYHtyfQ0KDQojZm9yIHByZWRpY3Rpb24gDQoNCnJlZzEgPC0gbG0oc2FsZV9wcmljZSB+IC4sIGRhdGEgPSBzdF9kcm9wX2dlb21ldHJ5KGRhdGFfb2cpICU+JSAgDQogICAgICAgICAgICAgZHBseXI6OmZpbHRlcih0b1ByZWRpY3QgPT0gIk1PREVMTElORyIpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCh2YXJpYWJsZXMpKQ0KDQpkYXRhX3ByZWQgPC0gc3RfZHJvcF9nZW9tZXRyeShkYXRhX29nKSU+JQ0KICBmaWx0ZXIodG9QcmVkaWN0PT0gIkNIQUxMRU5HRSIpDQogIA0KZGF0YV9wcmVkIDwtDQogIGRhdGFfcHJlZCAlPiUNCiAgbXV0YXRlKFByaWNlLlByZWRpY3QgPSBwcmVkaWN0KHJlZzEsZGF0YV9wcmVkKSkNCg0KZmluYWxfcHJlZCA8LSBzdF9kcm9wX2dlb21ldHJ5KGRhdGFfcHJlZCklPiUNCiAgc2VsZWN0KG11c2FJRCxQcmljZS5QcmVkaWN0KSU+JQ0KICBtdXRhdGUodGVhbSA9ICJTYW0gYW5kIFJvc2hpbmkiKQ0KDQojIHByZXR0eSByZWdyZXNzaW9uDQoNCnByaW50KHN0YXJnYXplcihyZWcxLCB0eXBlPSJ0ZXh0IiwgZGlnaXRzPTEsIHRpdGxlPSJMaW5lYXIgTW9kZWwgb2YgVHJhaW5pbmcgRGF0YXNldCAoRmlndXJlIDUpIiwgb3V0ID0gIlRyYWluaW5nIExNLnR4dCIpKQ0KDQpgYGANCg0KDQo=